From ef8dc2d360b56c8356b7b523627896225fdfce57 Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Mon, 16 Dec 2019 08:01:52 +0100 Subject: [PATCH 01/63] Draft linup panel tab extension point --- src/extensions.ts | 9 +- src/lineup/internal/LineUpPanelActions.ts | 159 ++++++++++++++++------ src/styles/_view_lineup.scss | 115 ++++++++++------ 3 files changed, 199 insertions(+), 84 deletions(-) diff --git a/src/extensions.ts b/src/extensions.ts index 355f2d791..2b07efe73 100644 --- a/src/extensions.ts +++ b/src/extensions.ts @@ -16,6 +16,7 @@ export const EXTENSION_POINT_TDP_SCORE = 'tdpScore'; export const EXTENSION_POINT_TDP_SCORE_IMPL = 'tdpScoreImpl'; export const EXTENSION_POINT_TDP_SCORE_LOADER = 'tdpScoreLoader'; export const EXTENSION_POINT_TDP_RANKING_BUTTON = 'tdpRankingButton'; +export const EXTENSION_POINT_TDP_LINEUP_PANEL_TAB = 'tdpLineupPanelTab'; export const EXTENSION_POINT_TDP_VIEW = 'tdpView'; export const EXTENSION_POINT_TDP_INSTANT_VIEW = 'tdpInstantView'; export const EXTENSION_POINT_TDP_APP_EXTENSION = 'tdpAppExtension'; @@ -198,14 +199,14 @@ export interface IView extends IEventHandler { /** * optional natural size used when stacking the view on top of each other */ - readonly naturalSize?: [number, number]|'auto'; + readonly naturalSize?: [number, number] | 'auto'; /** * initialized this view * @param {HTMLElement} params place to put parameter forms * @param {(name: string, value: any, previousValue: any) => Promise} onParameterChange instead of directly setting the parameter this method should be used to track the changes */ - init(params: HTMLElement, onParameterChange: (name: string, value: any, previousValue: any) => PromiseLike): PromiseLike|undefined; + init(params: HTMLElement, onParameterChange: (name: string, value: any, previousValue: any) => PromiseLike): PromiseLike | undefined; /** * changes the input selection as given to the constructor of this class @@ -297,7 +298,7 @@ export interface IViewPluginDesc extends IPluginDesc { /** * optional security check to show only certain views */ - security?: string|((user: IUser)=>boolean); + security?: string | ((user: IUser) => boolean); /** * a lot of topics/tags describing this view @@ -307,7 +308,7 @@ export interface IViewPluginDesc extends IPluginDesc { /** * a link to an external help page */ - helpUrl?: string | { url: string, linkText: string, title: string }; + helpUrl?: string | {url: string, linkText: string, title: string}; /** * as an alternative an help text shown as pop up */ diff --git a/src/lineup/internal/LineUpPanelActions.ts b/src/lineup/internal/LineUpPanelActions.ts index b53d51900..93ccdd5e9 100644 --- a/src/lineup/internal/LineUpPanelActions.ts +++ b/src/lineup/internal/LineUpPanelActions.ts @@ -5,13 +5,13 @@ import {IPlugin, IPluginDesc, list as listPlugins} from 'phovea_core/src/plugin' import {editDialog} from '../../storage'; import { IScoreLoader, EXTENSION_POINT_TDP_SCORE_LOADER, EXTENSION_POINT_TDP_SCORE, EXTENSION_POINT_TDP_RANKING_BUTTON, - IScoreLoaderExtensionDesc, IRankingButtonExtension, IRankingButtonExtensionDesc + IScoreLoaderExtensionDesc, IRankingButtonExtension, IRankingButtonExtensionDesc, EXTENSION_POINT_TDP_LINEUP_PANEL_TAB } from '../../extensions'; import {EventHandler} from 'phovea_core/src/event'; import {IARankingViewOptions} from '../ARankingView'; import {exportLogic} from './export'; import {lazyDialogModule} from '../../dialogs'; - +import * as $ from 'jquery'; export interface ISearchOption { text: string; id: string; @@ -39,6 +39,17 @@ export function wrap(score: IPluginDesc): IScoreLoader { } }; } +/** + * Basic markup of the side panel + * Adds functionality of tabs + */ +const sidePanelTemplate = ` +
+
+
+
+ +
`; export default class LineUpPanelActions extends EventHandler { static readonly EVENT_ZOOM_OUT = 'zoomOut'; @@ -61,11 +72,16 @@ export default class LineUpPanelActions extends EventHandler { readonly panel: SidePanel | null; readonly node: HTMLElement; + + private readonly header: HTMLElement; + private readonly body: HTMLElement; + private overview: HTMLElement; private wasCollapsed = false; constructor(protected readonly provider: LocalDataProvider, ctx: any, private readonly options: Readonly, doc = document) { super(); + this.node = doc.createElement('aside'); if (options.enableAddingColumns) { this.search = new SearchBox({ @@ -76,22 +92,28 @@ export default class LineUpPanelActions extends EventHandler { item.action(); }); } - + let lineUpSidePanel; if (this.options.enableSidePanel !== 'top') { this.panel = new SidePanel(ctx, doc, { chooser: false }); - this.node = this.panel.node; + this.node.classList.add('lu-side-panel'); + this.node.insertAdjacentHTML('afterbegin', sidePanelTemplate); + lineUpSidePanel = this.panel.node; } else { - this.node = doc.createElement('div'); - this.node.classList.add('lu-side-panel', 'lu-side-panel-top'); + lineUpSidePanel = doc.createElement('div'); + lineUpSidePanel.classList.add('lu-side-panel', 'lu-side-panel-top'); } - this.node.classList.add('tdp-view-lineup'); - this.collapse = options.enableSidePanel === 'top' || options.enableSidePanel === 'collapsed'; + this.header = this.node.querySelector('header'); + this.body = this.node.querySelector('.default'); + this.body.appendChild(lineUpSidePanel); + this.node.classList.add('tdp-view-lineup'); this.init(); + this.collapse = options.enableSidePanel === 'top' || options.enableSidePanel === 'collapsed'; } + forceCollapse() { this.wasCollapsed = this.collapse; this.collapse = true; @@ -112,9 +134,23 @@ export default class LineUpPanelActions extends EventHandler { } set collapse(value: boolean) { + this.switchButton(value); this.node.classList.toggle('collapsed', value); } + switchButton(value: boolean) { + const addColumnButton = this.header.querySelector('.lu-adder'); + const addColumnInput = this.body.querySelector('.lu-adder'); + + if (value && addColumnInput.contains(this.search.node)) { + addColumnInput.removeChild(this.search.node); + addColumnButton.appendChild(this.search.node); + } else if (!value) { + addColumnButton.removeChild(this.search.node); + addColumnInput.appendChild(this.search.node); + } + } + hide() { this.node.style.display = 'none'; } @@ -132,26 +168,35 @@ export default class LineUpPanelActions extends EventHandler { } private init() { - this.node.insertAdjacentHTML('afterbegin', ` -
-
${this.search ? '' : ''} -
`); - + const luAdder = ` +
${this.search ? '' : ''}
` + this.header.insertAdjacentHTML('afterbegin', luAdder); + this.node.querySelector('.lu-side-panel').insertAdjacentHTML('afterbegin', luAdder); if (!this.isTopMode && this.options.enableSidePanelCollapsing) { // top mode doesn't need collapse feature - this.node.insertAdjacentHTML('afterbegin', ``); + this.node.insertAdjacentHTML('afterbegin', ``); this.node.querySelector('a')!.addEventListener('click', (evt) => { evt.preventDefault(); evt.stopPropagation(); + $('.hello').tab('show'); this.collapse = !this.collapse; }); } - const buttons = this.node.querySelector('section'); + const buttons = this.header; this.appendExtraButtons().forEach((b) => buttons.appendChild(b)); - if(this.options.enableSaveRanking) { + this.appendExtraTabs().forEach((b) => { + b.href = '#b'; + this.header.appendChild(b); + const div = document.createElement('div'); + div.classList.add('tab-pane'); + div.id = 'b'; + div.innerText = 'hello from second tab'; + this.body.parentElement.appendChild(div); + }); + if (this.options.enableSaveRanking) { buttons.appendChild(this.appendSaveRanking()); } - if(this.options.enableDownload) { + if (this.options.enableDownload) { buttons.appendChild(this.appendDownload()); } if (this.options.enableZoom) { @@ -162,29 +207,57 @@ export default class LineUpPanelActions extends EventHandler { buttons.appendChild(this.appendOverviewButton()); } - const header = this.node.querySelector('.lu-adder')!; - - header.addEventListener('mouseleave', () => { - header.classList.remove('once'); + const addColumnButton = this.node.querySelector('.lu-adder')!; + // console.log(addColumnButton) + // console.log(AddInput) + addColumnButton.addEventListener('mouseleave', () => { + addColumnButton.classList.remove('once'); }); if (this.search) { - header.appendChild(this.search.node); - + addColumnButton.appendChild(this.search.node); this.node.querySelector('.lu-adder button')!.addEventListener('click', (evt) => { evt.preventDefault(); evt.stopPropagation(); if (!this.collapse) { return; } - header.classList.add('once'); + addColumnButton.classList.add('once'); (this.search.node.querySelector('input'))!.focus(); this.search.focus(); }); } } + private createTabMarkup(title: string, linkClass: string, onClick: (ranking: Ranking) => void, extraOptions?) { + const b = this.node.ownerDocument.createElement('a'); + b.className = linkClass; + b.setAttribute('data-toggle', 'tab'); + b.href = '#b'; + b.title = title; + b.addEventListener('click', (evt) => { + evt.stopPropagation(); + evt.preventDefault(); + const openTab = () => { + $('.fa-calculator').tab('show'); + this.collapse = false; + } + const openDefaultTab = () => { + if (this.node.querySelector('#b').classList.contains('active')) { + $('.hello').tab('show'); + return; + } + $('.fa-calculator').tab('show'); + } + this.collapse ? openTab() : openDefaultTab() + const first = this.provider.getRankings()[0]; + if (first) { + onClick(first); + } + }); + return b; + } - private createMarkup(title: string, linkClass: string, onClick: (ranking: Ranking) => void) { + private createMarkup(title: string, linkClass: string, onClick: (ranking: Ranking) => void, extraOptions?) { const b = this.node.ownerDocument.createElement('button'); b.className = linkClass; b.title = title; @@ -272,6 +345,16 @@ export default class LineUpPanelActions extends EventHandler { return this.createMarkup(button.title, 'fa ' + button.cssClass, listener); }); } + private appendExtraTabs() { + const buttons = listPlugins(EXTENSION_POINT_TDP_LINEUP_PANEL_TAB); + console.log(buttons) + return buttons.map((button) => { + const listener = () => { + button.load().then((p) => p.factory(p.desc, this.node)); + }; + return this.createTabMarkup(button.title, 'fa ' + button.cssClass, listener); + }); + } private downloadFile(content: BufferSource | Blob | string, mimeType: string, name: string) { const doc = this.node.ownerDocument; @@ -336,7 +419,7 @@ export default class LineUpPanelActions extends EventHandler { const items: (ISearchOption | IGroupSearchItem)[] = []; - if(this.options.enableAddingDatabaseColumns) { + if (this.options.enableAddingDatabaseColumns) { items.push(this.groupedDialog('Database Columns', this.getColumnDescription(descs, false))); } @@ -359,7 +442,7 @@ export default class LineUpPanelActions extends EventHandler { }); } - if(this.options.enableAddingPreviousColumns) { + if (this.options.enableAddingPreviousColumns) { const scoreDescs = this.getColumnDescription(descs, true); if (scoreDescs.length > 0) { items.push({ @@ -374,18 +457,18 @@ export default class LineUpPanelActions extends EventHandler { children: [] }; - if(this.options.enableAddingCombiningColumns) { + if (this.options.enableAddingCombiningColumns) { const combiningColumns = this.groupedDialog('Combining Columns', [ - { text: 'Weighted Sum', id: 'weightedSum', action: () => this.addColumn(createStackDesc('Weighted Sum')) }, - { text: 'Scripted Combination', id: 'scriptedCombination', action: () => this.addColumn(createScriptDesc('Scripted Combination')) }, - { text: 'Nested', id: 'nested', action: () => this.addColumn(createNestedDesc('Nested')) }, - { text: 'Min/Max/Mean Combination', id: 'reduce', action: () => this.addColumn(createReduceDesc()) }, - { text: 'Imposition', id: 'imposition', action: () => this.addColumn(createImpositionDesc()) } + {text: 'Weighted Sum', id: 'weightedSum', action: () => this.addColumn(createStackDesc('Weighted Sum'))}, + {text: 'Scripted Combination', id: 'scriptedCombination', action: () => this.addColumn(createScriptDesc('Scripted Combination'))}, + {text: 'Nested', id: 'nested', action: () => this.addColumn(createNestedDesc('Nested'))}, + {text: 'Min/Max/Mean Combination', id: 'reduce', action: () => this.addColumn(createReduceDesc())}, + {text: 'Imposition', id: 'imposition', action: () => this.addColumn(createImpositionDesc())} ]); specialColumnsOption.children.push(combiningColumns); } - if(this.options.enableAddingSupportColumns) { + if (this.options.enableAddingSupportColumns) { const supportColumns = this.groupedDialog('Support Columns', [ {text: 'Group Information', id: 'group', action: () => this.addColumn(createGroupDesc('Group'))}, {text: 'Selection Checkbox', id: 'selection', action: () => this.addColumn(createSelectionDesc())}, @@ -394,22 +477,22 @@ export default class LineUpPanelActions extends EventHandler { specialColumnsOption.children.push(supportColumns); } - if(this.options.enableAddingMetaDataColumns) { + if (this.options.enableAddingMetaDataColumns) { specialColumnsOption.children.push(...metaDataOptions); } // Only add special columns option if there are any items available - if(specialColumnsOption.children.length > 0) { + if (specialColumnsOption.children.length > 0) { items.push(specialColumnsOption); } this.search.data = items; } - private groupedDialog(text: string, children: ISearchOption[]):ISearchOption | IGroupSearchItem { + private groupedDialog(text: string, children: ISearchOption[]): ISearchOption | IGroupSearchItem { const viaDialog = this.options.enableAddingColumnGrouping; if (!viaDialog) { - return { text, children }; + return {text, children}; } return { text: `${text} …`, diff --git a/src/styles/_view_lineup.scss b/src/styles/_view_lineup.scss index 393a4d080..dfee3494e 100644 --- a/src/styles/_view_lineup.scss +++ b/src/styles/_view_lineup.scss @@ -11,21 +11,21 @@ line-height: 1; } - > div { + >div { flex: 1 1 auto; position: relative; } &.overview-detail { - > div { + >div { display: flex; flex-direction: column; - > header { + >header { flex: 0 0 auto; } - > div { + >div { flex: 1 1 auto; position: relative; } @@ -47,7 +47,7 @@ margin: 0; } - &.lu-filter-table > div:first-of-type > * { + &.lu-filter-table>div:first-of-type>* { flex: 0 0 auto; } } @@ -64,7 +64,7 @@ left: 0; overflow: auto; - > header { + >header { padding-bottom: 1px; // no idea otherwise scrollbar } @@ -87,14 +87,14 @@ $lu_assets: '~lineupjs/src/assets'; } .tdp-ranking-export-form-handle { - > span { + >span { visibility: hidden; padding: 0 0.5em 0 1em; cursor: grab; } - &:hover > span, - &.dragging > span { + &:hover>span, + &.dragging>span { visibility: visible; } @@ -105,7 +105,7 @@ $lu_assets: '~lineupjs/src/assets'; } } - .lu-side-panel > main > section::before { + .lu-side-panel>main>section::before { content: 'Column Summaries'; } @@ -113,10 +113,14 @@ $lu_assets: '~lineupjs/src/assets'; height: 19px; } + ////////////////////// + ///////////////////// .tdp-view-lineup.lu-side-panel { - flex: 0 0 auto; + display: flex; + flex-direction: column; position: relative; - + min-width: 20em; + overflow: hidden; %lu-panel-button { background: #fff; @@ -131,41 +135,54 @@ $lu_assets: '~lineupjs/src/assets'; } } - > section { - flex: 0 0 auto; + .lu-side-panel>main>section>div { + flex: 1; + } + + aside { + border-left: 0; + } + + >header { + align-self: flex-end; display: flex; - justify-content: flex-end; - margin-right: 1em; flex-wrap: wrap; + max-width: 19em; + .lu-adder { + margin-top: 1em; + display: none; + } + + a, button { @extend %lu-panel-button; + height: 2em; } + } - label, input { + label, + input { font-weight: normal; margin: 0; } - > div.lu-adder { + .lu-adder { margin-top: 1em; - > button { + button { @extend %lu-panel-button; display: none; } } - > a { + >a { @extend %lu-panel-button; - - position: absolute; - left: 0; - top: 0; width: 2em; height: 2em; + position: absolute; &::before { @include fa-icon(); @@ -207,48 +224,56 @@ $lu_assets: '~lineupjs/src/assets'; } .download-data-dropdown { + // show the number of selected rows after the element li[data-num-selected-rows]::after { - content: ' (' attr(data-num-selected-rows) ')'; + content: ' ('attr(data-num-selected-rows) ')'; display: inline; } // hide the dropdown-header and the next list point (= download link), if no rows are selected // note that this only works for a *single* link as after the header list item! li[data-num-selected-rows='0'], - li[data-num-selected-rows='0'] + li { + li[data-num-selected-rows='0']+li { display: none; } } + //////////////////// + //////////////////// + &.collapsed { max-width: 3em; min-width: 0; + display: flex; + flex-direction: column; - > a { - width: 100%; + >a { + width: 2.5em; order: 1; + position: relative; + align-self: flex-start; &::before { content: $fa-var-arrow-left; } } - > section { - order: 3; + header { + order: 2; + width: auto; flex-direction: column; margin-right: 0; + min-width: 0; + align-self: flex-start } - > div, - > .lu-stats, - > header, - > main { + .tab-content { display: none; } - > div.lu-adder { - order: 2; + .lu-adder { + order: 0; padding: 0; display: flex; flex-direction: column; @@ -260,7 +285,7 @@ $lu_assets: '~lineupjs/src/assets'; display: none; } - > button { + button { display: block; } @@ -280,7 +305,7 @@ $lu_assets: '~lineupjs/src/assets'; left: -15em; z-index: 2; - > ul { + ul { max-height: 50vh; } } @@ -292,10 +317,16 @@ $lu_assets: '~lineupjs/src/assets'; } } - .lu-sidepanel-top-form { + ////////////////////////////////////////////////// + ////////////////////////////////////////////////// + + + + + .lu-side-panel-top-form { display: flex; - > p { + p { flex: 1 1 0; } } @@ -308,7 +339,7 @@ $lu_assets: '~lineupjs/src/assets'; width: unset; float: right; - > section { + header { flex-direction: row; max-width: unset; } @@ -318,7 +349,7 @@ $lu_assets: '~lineupjs/src/assets'; margin-left: 1em; } - > div.lu-adder { + >div.lu-adder { margin-top: 0; &.once .lu-search { From b627a5e94d07658a54b2206812b8afb6fa36b1af Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Mon, 16 Dec 2019 12:45:34 +0100 Subject: [PATCH 02/63] Lineup panel refactoring --- src/lineup/internal/LineUpPanelActions.ts | 157 +++++++++++++++------- src/lineup/internal/utils.ts | 27 +++- src/styles/_view_lineup.scss | 4 +- 3 files changed, 136 insertions(+), 52 deletions(-) diff --git a/src/lineup/internal/LineUpPanelActions.ts b/src/lineup/internal/LineUpPanelActions.ts index 93ccdd5e9..ef4e83da4 100644 --- a/src/lineup/internal/LineUpPanelActions.ts +++ b/src/lineup/internal/LineUpPanelActions.ts @@ -12,6 +12,8 @@ import {IARankingViewOptions} from '../ARankingView'; import {exportLogic} from './export'; import {lazyDialogModule} from '../../dialogs'; import * as $ from 'jquery'; +import {threadId} from 'worker_threads'; + export interface ISearchOption { text: string; id: string; @@ -39,17 +41,69 @@ export function wrap(score: IPluginDesc): IScoreLoader { } }; } -/** - * Basic markup of the side panel - * Adds functionality of tabs - */ -const sidePanelTemplate = ` -
-
-
-
-
`; +class LineUpPanelHeader { + + readonly node: HTMLElement; + + constructor(parent: HTMLElement) { + this.node = parent.ownerDocument.createElement('header'); + parent.appendChild(this.node); + } + + + +} + + +class LineUpPanelTabContainer { + + readonly node: HTMLElement; + + private tabs: ALineUpPanelTab[] = []; + + constructor(parent: HTMLElement) { + this.node = parent.ownerDocument.createElement('main'); + this.node.classList.add('tab-content'); + parent.appendChild(this.node); + + } + + addTab(tab: ALineUpPanelTab) { + this.tabs = [...this.tabs, tab]; + this.node.appendChild(tab.node); + } + +} + +abstract class ALineUpPanelTab { + + readonly node: HTMLElement; + + constructor(parent: HTMLElement) { + this.node = parent.ownerDocument.createElement('div'); + this.node.classList.add('tab-pane'); + } + +} + +class LineUpSidePanelTab extends ALineUpPanelTab { + + readonly panel: SidePanel | null; + + constructor(parent: HTMLElement, ctx: any, doc = document) { + super(parent); + this.node.classList.add('active', 'default'); + this.node.id = 'side-panel-default'; + + this.panel = new SidePanel(ctx, doc, { + chooser: false + }); + + this.node.appendChild(this.panel.node); + } + +} export default class LineUpPanelActions extends EventHandler { static readonly EVENT_ZOOM_OUT = 'zoomOut'; @@ -71,17 +125,26 @@ export default class LineUpPanelActions extends EventHandler { private readonly search: SearchBox | null; readonly panel: SidePanel | null; - readonly node: HTMLElement; + readonly node: HTMLElement; // wrapper node - private readonly header: HTMLElement; - private readonly body: HTMLElement; + private readonly header: LineUpPanelHeader; + private readonly tabContainer: LineUpPanelTabContainer; + + + private body: HTMLElement; private overview: HTMLElement; private wasCollapsed = false; constructor(protected readonly provider: LocalDataProvider, ctx: any, private readonly options: Readonly, doc = document) { super(); + this.node = doc.createElement('aside'); + this.node.classList.add('lu-side-panel-wrapper'); + + this.header = new LineUpPanelHeader(this.node); + + // this.options.enableSidePanel = 'top'; if (options.enableAddingColumns) { this.search = new SearchBox({ @@ -92,23 +155,21 @@ export default class LineUpPanelActions extends EventHandler { item.action(); }); } - let lineUpSidePanel; - if (this.options.enableSidePanel !== 'top') { - this.panel = new SidePanel(ctx, doc, { - chooser: false - }); - this.node.classList.add('lu-side-panel'); - this.node.insertAdjacentHTML('afterbegin', sidePanelTemplate); - lineUpSidePanel = this.panel.node; + + if (this.options.enableSidePanel === 'top') { + this.node.classList.add('lu-side-panel-top'); + } else { - lineUpSidePanel = doc.createElement('div'); - lineUpSidePanel.classList.add('lu-side-panel', 'lu-side-panel-top'); + + this.tabContainer = new LineUpPanelTabContainer(this.node); + + const sidePanel = new LineUpSidePanelTab(this.node, ctx, doc); + this.tabContainer.addTab(sidePanel); + + this.body = sidePanel.node; // TODO remove + this.panel = sidePanel.panel; } - this.header = this.node.querySelector('header'); - this.body = this.node.querySelector('.default'); - this.body.appendChild(lineUpSidePanel); - this.node.classList.add('tdp-view-lineup'); this.init(); this.collapse = options.enableSidePanel === 'top' || options.enableSidePanel === 'collapsed'; } @@ -134,12 +195,12 @@ export default class LineUpPanelActions extends EventHandler { } set collapse(value: boolean) { - this.switchButton(value); + // this.switchButton(value); this.node.classList.toggle('collapsed', value); } - switchButton(value: boolean) { - const addColumnButton = this.header.querySelector('.lu-adder'); + private switchButton(value: boolean) { + const addColumnButton = this.header.node.querySelector('.lu-adder'); const addColumnInput = this.body.querySelector('.lu-adder'); if (value && addColumnInput.contains(this.search.node)) { @@ -168,10 +229,9 @@ export default class LineUpPanelActions extends EventHandler { } private init() { - const luAdder = ` -
${this.search ? '' : ''}
` - this.header.insertAdjacentHTML('afterbegin', luAdder); - this.node.querySelector('.lu-side-panel').insertAdjacentHTML('afterbegin', luAdder); + const luAdder = `
${this.search ? '' : ''}
`; + this.header.node.insertAdjacentHTML('afterbegin', luAdder); + this.body.insertAdjacentHTML('afterbegin', luAdder); if (!this.isTopMode && this.options.enableSidePanelCollapsing) { // top mode doesn't need collapse feature this.node.insertAdjacentHTML('afterbegin', ``); this.node.querySelector('a')!.addEventListener('click', (evt) => { @@ -182,17 +242,20 @@ export default class LineUpPanelActions extends EventHandler { }); } - const buttons = this.header; + const buttons = this.header.node; this.appendExtraButtons().forEach((b) => buttons.appendChild(b)); - this.appendExtraTabs().forEach((b) => { - b.href = '#b'; - this.header.appendChild(b); - const div = document.createElement('div'); - div.classList.add('tab-pane'); - div.id = 'b'; - div.innerText = 'hello from second tab'; - this.body.parentElement.appendChild(div); - }); + + if(!this.isTopMode) { + this.appendExtraTabs().forEach((b) => { + b.href = '#b'; + this.header.node.appendChild(b); + const div = document.createElement('div'); + div.classList.add('tab-pane'); + div.id = 'b'; + this.body.parentElement.appendChild(div); + }); + } + if (this.options.enableSaveRanking) { buttons.appendChild(this.appendSaveRanking()); } @@ -215,6 +278,8 @@ export default class LineUpPanelActions extends EventHandler { }); if (this.search) { + this.header.node.querySelector('.lu-adder').appendChild(this.search.node); + this.body.querySelector('.lu-adder').appendChild(this.search.node); addColumnButton.appendChild(this.search.node); this.node.querySelector('.lu-adder button')!.addEventListener('click', (evt) => { evt.preventDefault(); @@ -347,10 +412,9 @@ export default class LineUpPanelActions extends EventHandler { } private appendExtraTabs() { const buttons = listPlugins(EXTENSION_POINT_TDP_LINEUP_PANEL_TAB); - console.log(buttons) return buttons.map((button) => { const listener = () => { - button.load().then((p) => p.factory(p.desc, this.node)); + button.load().then((p) => p.factory(p.desc, this.node, this.provider)); }; return this.createTabMarkup(button.title, 'fa ' + button.cssClass, listener); }); @@ -554,6 +618,7 @@ export default class LineUpPanelActions extends EventHandler { } } + function findMappablePlugins(target: IDType, all: IPluginDesc[]) { if (!target) { return []; diff --git a/src/lineup/internal/utils.ts b/src/lineup/internal/utils.ts index 26668e6f4..d2c42ebd3 100644 --- a/src/lineup/internal/utils.ts +++ b/src/lineup/internal/utils.ts @@ -7,12 +7,30 @@ import {IDataRow} from 'lineupjs'; import {convertRow2MultiMap, IFormMultiMap, IFormRow} from '../../form'; import {encodeParams} from 'phovea_core/src/ajax'; + + +/** + * Checks wether the given function of type IAccessorFunc, i.e. of an AScoreAccessorProxy. + * Beware: coding horrors await beyond this function header. + * @param accessor + */ +export function isProxyAccessor(accessor: any): accessor is IAccessorFunc { + if (accessor && typeof (accessor) === 'function' && accessor.length === 1) { + return accessor.toString() === '(row) => this.access(row.v)'; + } + return false; +} + +export interface IAccessorFunc { + (row: IDataRow): T; +} + export class AScoreAccessorProxy { /** * the accessor for the score column * @param row */ - readonly accessor = (row: IDataRow) => this.access(row.v); + readonly accessor: IAccessorFunc = (row: IDataRow) => this.access(row.v); private readonly scores = new Map(); constructor(private readonly missingValue: T = null) { @@ -64,7 +82,7 @@ export function createAccessor(colDesc: any): AScoreAccessorProxy { * converts the given filter object to request params * @param filter input filter */ -export function toFilter(filter: IFormMultiMap|IFormRow[]): IParams { +export function toFilter(filter: IFormMultiMap | IFormRow[]): IParams { if (Array.isArray(filter)) { //map first return toFilter(convertRow2MultiMap(filter)); @@ -101,12 +119,13 @@ export function toFilterString(filter: IFormMultiMap, key2name?: Mapany): (rows: IFormRow[])=>Promise { +export function previewFilterHint(database: string, view: string, extraParams?: () => any): (rows: IFormRow[]) => Promise { let total: Promise = null; const cache = new Map>(); diff --git a/src/styles/_view_lineup.scss b/src/styles/_view_lineup.scss index dfee3494e..1fc1f6f0f 100644 --- a/src/styles/_view_lineup.scss +++ b/src/styles/_view_lineup.scss @@ -115,7 +115,7 @@ $lu_assets: '~lineupjs/src/assets'; ////////////////////// ///////////////////// - .tdp-view-lineup.lu-side-panel { + .lu-side-panel-wrapper { display: flex; flex-direction: column; position: relative; @@ -331,7 +331,7 @@ $lu_assets: '~lineupjs/src/assets'; } } - .lu-side-panel-top.tdp-view-lineup.lu-side-panel.collapsed { + .lu-side-panel-top.lu-side-panel-wrapper.collapsed { display: inline-flex; flex-direction: row; z-index: 100; From 971d9d1925e2d10570e505471eec1e0e4c38397c Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Mon, 16 Dec 2019 14:02:02 +0100 Subject: [PATCH 03/63] Further LineUpPanel refactoring --- src/lineup/internal/LineUpPanelActions.ts | 210 ++++++++++++---------- 1 file changed, 113 insertions(+), 97 deletions(-) diff --git a/src/lineup/internal/LineUpPanelActions.ts b/src/lineup/internal/LineUpPanelActions.ts index ef4e83da4..986708b8b 100644 --- a/src/lineup/internal/LineUpPanelActions.ts +++ b/src/lineup/internal/LineUpPanelActions.ts @@ -42,19 +42,99 @@ export function wrap(score: IPluginDesc): IScoreLoader { }; } +interface ILineUpPanelButton { + readonly node: HTMLElement; +} + class LineUpPanelHeader { readonly node: HTMLElement; + private buttons: ILineUpPanelButton[] = []; + constructor(parent: HTMLElement) { this.node = parent.ownerDocument.createElement('header'); parent.appendChild(this.node); } + addButton(button: ILineUpPanelButton) { + this.buttons = [...this.buttons, button]; + this.node.appendChild(button.node); + } +} + + +class LineUpPanelButton implements ILineUpPanelButton { + readonly node: HTMLElement; + constructor(parent: HTMLElement, private provider: LocalDataProvider, title: string, linkClass: string, onClick: (ranking: Ranking) => void, extraOptions?) { + this.node = parent.ownerDocument.createElement('button'); + this.node.className = linkClass; + this.node.title = title; + this.node.addEventListener('click', (evt) => { + evt.stopPropagation(); + evt.preventDefault(); + const first = this.provider.getRankings()[0]; + if (first) { + onClick(first); + } + }); + } } +class LineUpPanelDownloadButton implements ILineUpPanelButton { + readonly node: HTMLElement; + + constructor(parent: HTMLElement, private provider: LocalDataProvider, isTopMode: boolean) { + this.node = parent.ownerDocument.createElement('div'); + this.node.classList.add('btn-group', 'download-data-dropdown'); + this.node.innerHTML = ` + + + `; + + // Listen for row selection and update number of selected rows + // Show/hide some dropdown menu points accordingly using CSS + this.provider.on(LocalDataProvider.EVENT_SELECTION_CHANGED + '.download-menu', (indices: number[]) => { + (this.node.querySelector('[data-num-selected-rows]')).dataset.numSelectedRows = indices.length.toString(); + }); + + const links = Array.from(this.node.querySelectorAll('a')); + for (const link of links) { + link.onclick = (evt) => { + evt.preventDefault(); + evt.stopPropagation(); + const type = link.dataset.t; + const onlySelected = link.dataset.s === 's'; + exportLogic(type, onlySelected, this.provider).then(({content, mimeType, name}) => { + this.downloadFile(content, mimeType, name); + }); + }; + } + } + + private downloadFile(content: BufferSource | Blob | string, mimeType: string, name: string) { + const doc = this.node.ownerDocument; + const downloadLink = doc.createElement('a'); + const blob = new Blob([content], {type: mimeType}); + downloadLink.href = URL.createObjectURL(blob); + (downloadLink).download = name; + + doc.body.appendChild(downloadLink); + downloadLink.click(); + downloadLink.remove(); + } + +} class LineUpPanelTabContainer { @@ -243,7 +323,7 @@ export default class LineUpPanelActions extends EventHandler { } const buttons = this.header.node; - this.appendExtraButtons().forEach((b) => buttons.appendChild(b)); + this.appendExtraButtons(buttons); if(!this.isTopMode) { this.appendExtraTabs().forEach((b) => { @@ -257,17 +337,38 @@ export default class LineUpPanelActions extends EventHandler { } if (this.options.enableSaveRanking) { - buttons.appendChild(this.appendSaveRanking()); + const listener = (ranking: Ranking) => { + editDialog(null, (name, description, sec) => { + this.fire(LineUpPanelActions.EVENT_SAVE_NAMED_SET, ranking.getOrder(), name, description, sec); + }); + }; + + const saveRankingButton = new LineUpPanelButton(buttons, this.provider, 'Save List of Entities', 'fa fa-save', listener); + this.header.addButton(saveRankingButton); } + if (this.options.enableDownload) { - buttons.appendChild(this.appendDownload()); + const downloadButton = new LineUpPanelDownloadButton(buttons, this.provider, this.isTopMode); + this.header.addButton(downloadButton); } + if (this.options.enableZoom) { - buttons.appendChild(this.createMarkup('Zoom In', 'fa fa-search-plus gap', () => this.fire(LineUpPanelActions.EVENT_ZOOM_IN))); - buttons.appendChild(this.createMarkup('Zoom Out', 'fa fa-search-minus', () => this.fire(LineUpPanelActions.EVENT_ZOOM_OUT))); + const zoomInButton = new LineUpPanelButton(buttons, this.provider, 'Zoom In', 'fa fa-search-plus gap', () => this.fire(LineUpPanelActions.EVENT_ZOOM_IN)); + this.header.addButton(zoomInButton); + + const zoomOutButton = new LineUpPanelButton(buttons, this.provider, 'Zoom Out', 'fa fa-search-minus', () => this.fire(LineUpPanelActions.EVENT_ZOOM_OUT)); + this.header.addButton(zoomOutButton); } + if (this.options.enableOverviewMode) { - buttons.appendChild(this.appendOverviewButton()); + const listener = () => { + const selected = this.overview.classList.toggle('fa-th-list'); + this.overview.classList.toggle('fa-list'); + this.fire(LineUpPanelActions.EVENT_RULE_CHANGED, selected ? rule : null); + }; + const overviewButton = new LineUpPanelButton(buttons, this.provider, 'En/Disable Overview', this.options.enableOverviewMode === 'active' ? 'fa fa-th-list' : 'fa fa-list', listener); + this.overview = overviewButton.node; // TODO might be removed + this.header.addButton(overviewButton); } const addColumnButton = this.node.querySelector('.lu-adder')!; @@ -293,6 +394,7 @@ export default class LineUpPanelActions extends EventHandler { }); } } + private createTabMarkup(title: string, linkClass: string, onClick: (ranking: Ranking) => void, extraOptions?) { const b = this.node.ownerDocument.createElement('a'); b.className = linkClass; @@ -322,30 +424,6 @@ export default class LineUpPanelActions extends EventHandler { return b; } - private createMarkup(title: string, linkClass: string, onClick: (ranking: Ranking) => void, extraOptions?) { - const b = this.node.ownerDocument.createElement('button'); - b.className = linkClass; - b.title = title; - b.addEventListener('click', (evt) => { - evt.stopPropagation(); - evt.preventDefault(); - const first = this.provider.getRankings()[0]; - if (first) { - onClick(first); - } - }); - return b; - } - - private appendOverviewButton() { - const listener = () => { - const selected = this.overview.classList.toggle('fa-th-list'); - this.overview.classList.toggle('fa-list'); - this.fire(LineUpPanelActions.EVENT_RULE_CHANGED, selected ? rule : null); - }; - return this.overview = this.createMarkup('En/Disable Overview', this.options.enableOverviewMode === 'active' ? 'fa fa-th-list' : 'fa fa-list', listener); - } - setViolation(violation?: string) { if (violation) { this.overview.dataset.violation = violation; @@ -354,62 +432,18 @@ export default class LineUpPanelActions extends EventHandler { } } - private appendDownload() { - const node = this.node.ownerDocument.createElement('div'); - node.classList.add('btn-group', 'download-data-dropdown'); - node.innerHTML = ` - - - `; - - // Listen for row selection and update number of selected rows - // Show/hide some dropdown menu points accordingly using CSS - this.provider.on(LocalDataProvider.EVENT_SELECTION_CHANGED + '.download-menu', (indices: number[]) => { - (node.querySelector('[data-num-selected-rows]')).dataset.numSelectedRows = indices.length.toString(); - }); - - const links = Array.from(node.querySelectorAll('a')); - for (const link of links) { - link.onclick = (evt) => { - evt.preventDefault(); - evt.stopPropagation(); - const type = link.dataset.t; - const onlySelected = link.dataset.s === 's'; - exportLogic(type, onlySelected, this.provider).then(({content, mimeType, name}) => { - this.downloadFile(content, mimeType, name); - }); - }; - } - - - return node; - } - - private appendSaveRanking() { - const listener = (ranking: Ranking) => { - this.saveRankingDialog(ranking.getOrder()); - }; - - return this.createMarkup('Save List of Entities', 'fa fa-save', listener); - } - - private appendExtraButtons() { + private appendExtraButtons(parent: HTMLElement) { const buttons = listPlugins(EXTENSION_POINT_TDP_RANKING_BUTTON); return buttons.map((button) => { const listener = () => { button.load().then((p) => this.scoreColumnDialog(p)); }; - return this.createMarkup(button.title, 'fa ' + button.cssClass, listener); + + const luButton = new LineUpPanelButton(parent, this.provider, button.title, 'fa ' + button.cssClass, listener); + this.header.addButton(luButton); }); } + private appendExtraTabs() { const buttons = listPlugins(EXTENSION_POINT_TDP_LINEUP_PANEL_TAB); return buttons.map((button) => { @@ -420,24 +454,6 @@ export default class LineUpPanelActions extends EventHandler { }); } - private downloadFile(content: BufferSource | Blob | string, mimeType: string, name: string) { - const doc = this.node.ownerDocument; - const downloadLink = doc.createElement('a'); - const blob = new Blob([content], {type: mimeType}); - downloadLink.href = URL.createObjectURL(blob); - (downloadLink).download = name; - - doc.body.appendChild(downloadLink); - downloadLink.click(); - downloadLink.remove(); - } - - protected saveRankingDialog(order: number[]) { - editDialog(null, (name, description, sec) => { - this.fire(LineUpPanelActions.EVENT_SAVE_NAMED_SET, order, name, description, sec); - }); - } - private resolveArgs() { return typeof this.options.additionalScoreParameter === 'function' ? this.options.additionalScoreParameter() : this.options.additionalScoreParameter; } From 6e2e1c6a6665587b6f7c719d2183df843368dc5f Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Mon, 16 Dec 2019 15:33:32 +0100 Subject: [PATCH 04/63] further refactoring --- src/lineup/internal/LineUpPanelActions.ts | 152 +++++++++++++--------- src/styles/_view_lineup.scss | 16 +-- 2 files changed, 94 insertions(+), 74 deletions(-) diff --git a/src/lineup/internal/LineUpPanelActions.ts b/src/lineup/internal/LineUpPanelActions.ts index 986708b8b..03988c5cb 100644 --- a/src/lineup/internal/LineUpPanelActions.ts +++ b/src/lineup/internal/LineUpPanelActions.ts @@ -1,5 +1,5 @@ -import {SidePanel, spaceFillingRule, IGroupSearchItem, SearchBox, LocalDataProvider, createStackDesc, IColumnDesc, createScriptDesc, createSelectionDesc, createAggregateDesc, createGroupDesc, Ranking, createImpositionDesc, createNestedDesc, createReduceDesc, isSupportType, Column} from 'lineupjs'; +import {SidePanel, spaceFillingRule, IGroupSearchItem, SearchBox, LocalDataProvider, createStackDesc, IColumnDesc, createScriptDesc, createSelectionDesc, createAggregateDesc, createGroupDesc, Ranking, createImpositionDesc, createNestedDesc, createReduceDesc, isSupportType, Column, IItem} from 'lineupjs'; import {IDType, resolve} from 'phovea_core/src/idtype'; import {IPlugin, IPluginDesc, list as listPlugins} from 'phovea_core/src/plugin'; import {editDialog} from '../../storage'; @@ -42,6 +42,40 @@ export function wrap(score: IPluginDesc): IScoreLoader { }; } +class LineUpSearchBoxProvider { + + private searchBoxes: SearchBox[] = []; + + private idType: IDType | null = null; + + constructor(private provider: LocalDataProvider, private options: any) { + + } + + get length(): number { + return this.searchBoxes.length; + } + + createSearchBox(): SearchBox { + const searchBox = new SearchBox({ + placeholder: 'Add Column...' + }); + + searchBox.on(SearchBox.EVENT_SELECT, (item) => { + item.action(); + }); + + this.searchBoxes = [...this.searchBoxes, searchBox]; + + return searchBox; + } + + update(items: (ISearchOption | IGroupSearchItem)[]) { + this.searchBoxes.forEach((searchBox) => searchBox.data = items); + } +} + + interface ILineUpPanelButton { readonly node: HTMLElement; } @@ -136,6 +170,38 @@ class LineUpPanelDownloadButton implements ILineUpPanelButton { } +class LineUpPanelAddColumnButton implements ILineUpPanelButton { + readonly node: HTMLElement; + + constructor(parent: HTMLElement, private readonly search: SearchBox) { + this.node = parent.ownerDocument.createElement('div'); + this.node.classList.add('lu-adder', 'once'); + + this.node.addEventListener('mouseleave', () => { + this.node.classList.remove('once'); + }); + + const button = this.node.ownerDocument.createElement('button'); + button.classList.add('fa', 'fa-plus'); + button.title = 'Add Column'; + + button.addEventListener('click', (evt) => { + evt.preventDefault(); + evt.stopPropagation(); + // if (!this.collapse) { + // return; + // } + //this.node.classList.add('once'); + (this.search.node.querySelector('input'))!.focus(); + this.search.focus(); + }); + + this.node.appendChild(button); + this.node.appendChild(this.search.node); + } +} + + class LineUpPanelTabContainer { readonly node: HTMLElement; @@ -171,7 +237,7 @@ class LineUpSidePanelTab extends ALineUpPanelTab { readonly panel: SidePanel | null; - constructor(parent: HTMLElement, ctx: any, doc = document) { + constructor(parent: HTMLElement, private readonly search: SearchBox, ctx: any, doc = document) { super(parent); this.node.classList.add('active', 'default'); this.node.id = 'side-panel-default'; @@ -180,11 +246,13 @@ class LineUpSidePanelTab extends ALineUpPanelTab { chooser: false }); + this.node.appendChild(this.search.node); this.node.appendChild(this.panel.node); } } + export default class LineUpPanelActions extends EventHandler { static readonly EVENT_ZOOM_OUT = 'zoomOut'; static readonly EVENT_ZOOM_IN = 'zoomIn'; @@ -202,7 +270,7 @@ export default class LineUpPanelActions extends EventHandler { private idType: IDType | null = null; - private readonly search: SearchBox | null; + private readonly searchBoxProvider: LineUpSearchBoxProvider; readonly panel: SidePanel | null; readonly node: HTMLElement; // wrapper node @@ -224,17 +292,9 @@ export default class LineUpPanelActions extends EventHandler { this.header = new LineUpPanelHeader(this.node); - // this.options.enableSidePanel = 'top'; + this.searchBoxProvider = new LineUpSearchBoxProvider(provider, options); - if (options.enableAddingColumns) { - this.search = new SearchBox({ - placeholder: 'Add Column...' - }); - this.search.on(SearchBox.EVENT_SELECT, (item) => { - this.node.querySelector('.lu-adder')!.classList.remove('once'); - item.action(); - }); - } + // this.options.enableSidePanel = 'top'; if (this.options.enableSidePanel === 'top') { this.node.classList.add('lu-side-panel-top'); @@ -243,7 +303,7 @@ export default class LineUpPanelActions extends EventHandler { this.tabContainer = new LineUpPanelTabContainer(this.node); - const sidePanel = new LineUpSidePanelTab(this.node, ctx, doc); + const sidePanel = new LineUpSidePanelTab(this.node, this.searchBoxProvider.createSearchBox(), ctx, doc); this.tabContainer.addTab(sidePanel); this.body = sidePanel.node; // TODO remove @@ -275,23 +335,9 @@ export default class LineUpPanelActions extends EventHandler { } set collapse(value: boolean) { - // this.switchButton(value); this.node.classList.toggle('collapsed', value); } - private switchButton(value: boolean) { - const addColumnButton = this.header.node.querySelector('.lu-adder'); - const addColumnInput = this.body.querySelector('.lu-adder'); - - if (value && addColumnInput.contains(this.search.node)) { - addColumnInput.removeChild(this.search.node); - addColumnButton.appendChild(this.search.node); - } else if (!value) { - addColumnButton.removeChild(this.search.node); - addColumnInput.appendChild(this.search.node); - } - } - hide() { this.node.style.display = 'none'; } @@ -309,20 +355,22 @@ export default class LineUpPanelActions extends EventHandler { } private init() { - const luAdder = `
${this.search ? '' : ''}
`; - this.header.node.insertAdjacentHTML('afterbegin', luAdder); - this.body.insertAdjacentHTML('afterbegin', luAdder); + const buttons = this.header.node; + if (!this.isTopMode && this.options.enableSidePanelCollapsing) { // top mode doesn't need collapse feature - this.node.insertAdjacentHTML('afterbegin', ``); - this.node.querySelector('a')!.addEventListener('click', (evt) => { - evt.preventDefault(); - evt.stopPropagation(); - $('.hello').tab('show'); + const listener = () => { this.collapse = !this.collapse; - }); + }; + + const collapseButton = new LineUpPanelButton(buttons, this.provider, '(Un)Collapse', 'collapse-button', listener); + this.header.addButton(collapseButton); + } + + if (this.options.enableAddingColumns) { + const addColumnButton = new LineUpPanelAddColumnButton(buttons, this.searchBoxProvider.createSearchBox()); + this.header.addButton(addColumnButton); } - const buttons = this.header.node; this.appendExtraButtons(buttons); if(!this.isTopMode) { @@ -370,29 +418,6 @@ export default class LineUpPanelActions extends EventHandler { this.overview = overviewButton.node; // TODO might be removed this.header.addButton(overviewButton); } - - const addColumnButton = this.node.querySelector('.lu-adder')!; - // console.log(addColumnButton) - // console.log(AddInput) - addColumnButton.addEventListener('mouseleave', () => { - addColumnButton.classList.remove('once'); - }); - - if (this.search) { - this.header.node.querySelector('.lu-adder').appendChild(this.search.node); - this.body.querySelector('.lu-adder').appendChild(this.search.node); - addColumnButton.appendChild(this.search.node); - this.node.querySelector('.lu-adder button')!.addEventListener('click', (evt) => { - evt.preventDefault(); - evt.stopPropagation(); - if (!this.collapse) { - return; - } - addColumnButton.classList.add('once'); - (this.search.node.querySelector('input'))!.focus(); - this.search.focus(); - }); - } } private createTabMarkup(title: string, linkClass: string, onClick: (ranking: Ranking) => void, extraOptions?) { @@ -492,9 +517,10 @@ export default class LineUpPanelActions extends EventHandler { async updateChooser(idType: IDType, descs: IColumnDesc[]) { this.idType = idType; - if (!this.search) { + if (this.searchBoxProvider.length === 0) { return; } + const {metaDataOptions, loadedScorePlugins} = await this.resolveScores(this.idType); const items: (ISearchOption | IGroupSearchItem)[] = []; @@ -566,7 +592,7 @@ export default class LineUpPanelActions extends EventHandler { items.push(specialColumnsOption); } - this.search.data = items; + this.searchBoxProvider.update(items); } private groupedDialog(text: string, children: ISearchOption[]): ISearchOption | IGroupSearchItem { diff --git a/src/styles/_view_lineup.scss b/src/styles/_view_lineup.scss index 1fc1f6f0f..86f109624 100644 --- a/src/styles/_view_lineup.scss +++ b/src/styles/_view_lineup.scss @@ -144,10 +144,7 @@ $lu_assets: '~lineupjs/src/assets'; } >header { - align-self: flex-end; display: flex; - flex-wrap: wrap; - max-width: 19em; .lu-adder { margin-top: 1em; @@ -178,11 +175,12 @@ $lu_assets: '~lineupjs/src/assets'; } } - >a { + .collapse-button { @extend %lu-panel-button; - width: 2em; + // width: 2em; height: 2em; - position: absolute; + // position: absolute; + margin-right: 1em; &::before { @include fa-icon(); @@ -248,11 +246,9 @@ $lu_assets: '~lineupjs/src/assets'; display: flex; flex-direction: column; - >a { + .collapse-button { width: 2.5em; - order: 1; position: relative; - align-self: flex-start; &::before { content: $fa-var-arrow-left; @@ -260,7 +256,6 @@ $lu_assets: '~lineupjs/src/assets'; } header { - order: 2; width: auto; flex-direction: column; margin-right: 0; @@ -273,7 +268,6 @@ $lu_assets: '~lineupjs/src/assets'; } .lu-adder { - order: 0; padding: 0; display: flex; flex-direction: column; From 4c36b2edaef486ff482d842182fcd852be48141e Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Mon, 16 Dec 2019 16:03:30 +0100 Subject: [PATCH 05/63] Fix open/close add column dialog --- src/lineup/internal/LineUpPanelActions.ts | 7 ++----- src/styles/_view_lineup.scss | 6 ++---- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/lineup/internal/LineUpPanelActions.ts b/src/lineup/internal/LineUpPanelActions.ts index 03988c5cb..a1dae1713 100644 --- a/src/lineup/internal/LineUpPanelActions.ts +++ b/src/lineup/internal/LineUpPanelActions.ts @@ -175,7 +175,7 @@ class LineUpPanelAddColumnButton implements ILineUpPanelButton { constructor(parent: HTMLElement, private readonly search: SearchBox) { this.node = parent.ownerDocument.createElement('div'); - this.node.classList.add('lu-adder', 'once'); + this.node.classList.add('lu-adder'); this.node.addEventListener('mouseleave', () => { this.node.classList.remove('once'); @@ -188,10 +188,7 @@ class LineUpPanelAddColumnButton implements ILineUpPanelButton { button.addEventListener('click', (evt) => { evt.preventDefault(); evt.stopPropagation(); - // if (!this.collapse) { - // return; - // } - //this.node.classList.add('once'); + this.node.classList.add('once'); (this.search.node.querySelector('input'))!.focus(); this.search.focus(); }); diff --git a/src/styles/_view_lineup.scss b/src/styles/_view_lineup.scss index 86f109624..15931b59a 100644 --- a/src/styles/_view_lineup.scss +++ b/src/styles/_view_lineup.scss @@ -119,8 +119,6 @@ $lu_assets: '~lineupjs/src/assets'; display: flex; flex-direction: column; position: relative; - min-width: 20em; - overflow: hidden; %lu-panel-button { background: #fff; @@ -249,6 +247,7 @@ $lu_assets: '~lineupjs/src/assets'; .collapse-button { width: 2.5em; position: relative; + margin-right: 0; &::before { content: $fa-var-arrow-left; @@ -260,7 +259,7 @@ $lu_assets: '~lineupjs/src/assets'; flex-direction: column; margin-right: 0; min-width: 0; - align-self: flex-start + align-self: flex-start; } .tab-content { @@ -273,7 +272,6 @@ $lu_assets: '~lineupjs/src/assets'; flex-direction: column; position: relative; border: none; - margin-top: 3em; &:not(.once) .lu-search { display: none; From 4909ec3a86e46c4b80c9f7c2145d3d1325acc71b Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Mon, 16 Dec 2019 17:40:31 +0100 Subject: [PATCH 06/63] Refactor ranking button and tab container --- src/lineup/internal/LineUpPanelActions.ts | 173 +++++++++++++--------- 1 file changed, 107 insertions(+), 66 deletions(-) diff --git a/src/lineup/internal/LineUpPanelActions.ts b/src/lineup/internal/LineUpPanelActions.ts index a1dae1713..0fe2d2449 100644 --- a/src/lineup/internal/LineUpPanelActions.ts +++ b/src/lineup/internal/LineUpPanelActions.ts @@ -102,7 +102,22 @@ class LineUpPanelHeader { class LineUpPanelButton implements ILineUpPanelButton { readonly node: HTMLElement; - constructor(parent: HTMLElement, private provider: LocalDataProvider, title: string, linkClass: string, onClick: (ranking: Ranking) => void, extraOptions?) { + constructor(parent: HTMLElement, title: string, linkClass: string, onClick: () => void) { + this.node = parent.ownerDocument.createElement('button'); + this.node.className = linkClass; + this.node.title = title; + this.node.addEventListener('click', (evt) => { + evt.stopPropagation(); + evt.preventDefault(); + onClick(); + }); + } +} + +class LineUpPanelRankingButton implements ILineUpPanelButton { + readonly node: HTMLElement; + + constructor(parent: HTMLElement, private provider: LocalDataProvider, title: string, linkClass: string, onClick: (ranking: Ranking) => void) { this.node = parent.ownerDocument.createElement('button'); this.node.className = linkClass; this.node.title = title; @@ -203,41 +218,83 @@ class LineUpPanelTabContainer { readonly node: HTMLElement; - private tabs: ALineUpPanelTab[] = []; + private tabs: LineUpPanelTab[] = []; + + private currentTab: LineUpPanelTab; constructor(parent: HTMLElement) { this.node = parent.ownerDocument.createElement('main'); this.node.classList.add('tab-content'); parent.appendChild(this.node); + } + private get defaultTab(): LineUpPanelTab { + return this.tabs[0]; } - addTab(tab: ALineUpPanelTab) { + addTab(tab: LineUpPanelTab) { this.tabs = [...this.tabs, tab]; this.node.appendChild(tab.node); } + toggle(tab: LineUpPanelTab) { + if(this.currentTab === tab) { + this.hide(tab); + + } else { + this.show(tab); + } + } + + show(tab: LineUpPanelTab) { + if(this.currentTab) { + this.currentTab.hide(); + } + + tab.show(); + this.currentTab = tab; + } + + hide(tab: LineUpPanelTab) { + tab.hide(); + this.defaultTab.show(); + this.currentTab = this.defaultTab; + } + +} + + +interface ILineUpPanelTabOptions { + width: string; } -abstract class ALineUpPanelTab { +class LineUpPanelTab { readonly node: HTMLElement; - constructor(parent: HTMLElement) { + constructor(parent: HTMLElement, options?: Partial) { this.node = parent.ownerDocument.createElement('div'); this.node.classList.add('tab-pane'); + + const o = Object.assign({}, options); + this.node.style.width = o.width || null; } + show() { + this.node.classList.add('active'); + } + + hide() { + this.node.classList.remove('active'); + } } -class LineUpSidePanelTab extends ALineUpPanelTab { +class LineUpSidePanelTab extends LineUpPanelTab { readonly panel: SidePanel | null; constructor(parent: HTMLElement, private readonly search: SearchBox, ctx: any, doc = document) { super(parent); - this.node.classList.add('active', 'default'); - this.node.id = 'side-panel-default'; this.panel = new SidePanel(ctx, doc, { chooser: false @@ -275,9 +332,6 @@ export default class LineUpPanelActions extends EventHandler { private readonly header: LineUpPanelHeader; private readonly tabContainer: LineUpPanelTabContainer; - - private body: HTMLElement; - private overview: HTMLElement; private wasCollapsed = false; @@ -297,14 +351,12 @@ export default class LineUpPanelActions extends EventHandler { this.node.classList.add('lu-side-panel-top'); } else { + const sidePanel = new LineUpSidePanelTab(this.node, this.searchBoxProvider.createSearchBox(), ctx, doc); + this.panel = sidePanel.panel; this.tabContainer = new LineUpPanelTabContainer(this.node); - - const sidePanel = new LineUpSidePanelTab(this.node, this.searchBoxProvider.createSearchBox(), ctx, doc); this.tabContainer.addTab(sidePanel); - - this.body = sidePanel.node; // TODO remove - this.panel = sidePanel.panel; + this.tabContainer.show(sidePanel); } this.init(); @@ -359,7 +411,7 @@ export default class LineUpPanelActions extends EventHandler { this.collapse = !this.collapse; }; - const collapseButton = new LineUpPanelButton(buttons, this.provider, '(Un)Collapse', 'collapse-button', listener); + const collapseButton = new LineUpPanelButton(buttons, '(Un)Collapse', 'collapse-button', listener); this.header.addButton(collapseButton); } @@ -370,17 +422,6 @@ export default class LineUpPanelActions extends EventHandler { this.appendExtraButtons(buttons); - if(!this.isTopMode) { - this.appendExtraTabs().forEach((b) => { - b.href = '#b'; - this.header.node.appendChild(b); - const div = document.createElement('div'); - div.classList.add('tab-pane'); - div.id = 'b'; - this.body.parentElement.appendChild(div); - }); - } - if (this.options.enableSaveRanking) { const listener = (ranking: Ranking) => { editDialog(null, (name, description, sec) => { @@ -388,7 +429,7 @@ export default class LineUpPanelActions extends EventHandler { }); }; - const saveRankingButton = new LineUpPanelButton(buttons, this.provider, 'Save List of Entities', 'fa fa-save', listener); + const saveRankingButton = new LineUpPanelRankingButton(buttons, this.provider, 'Save List of Entities', 'fa fa-save', listener); this.header.addButton(saveRankingButton); } @@ -398,10 +439,10 @@ export default class LineUpPanelActions extends EventHandler { } if (this.options.enableZoom) { - const zoomInButton = new LineUpPanelButton(buttons, this.provider, 'Zoom In', 'fa fa-search-plus gap', () => this.fire(LineUpPanelActions.EVENT_ZOOM_IN)); + const zoomInButton = new LineUpPanelButton(buttons, 'Zoom In', 'fa fa-search-plus gap', () => this.fire(LineUpPanelActions.EVENT_ZOOM_IN)); this.header.addButton(zoomInButton); - const zoomOutButton = new LineUpPanelButton(buttons, this.provider, 'Zoom Out', 'fa fa-search-minus', () => this.fire(LineUpPanelActions.EVENT_ZOOM_OUT)); + const zoomOutButton = new LineUpPanelButton(buttons, 'Zoom Out', 'fa fa-search-minus', () => this.fire(LineUpPanelActions.EVENT_ZOOM_OUT)); this.header.addButton(zoomOutButton); } @@ -411,39 +452,16 @@ export default class LineUpPanelActions extends EventHandler { this.overview.classList.toggle('fa-list'); this.fire(LineUpPanelActions.EVENT_RULE_CHANGED, selected ? rule : null); }; - const overviewButton = new LineUpPanelButton(buttons, this.provider, 'En/Disable Overview', this.options.enableOverviewMode === 'active' ? 'fa fa-th-list' : 'fa fa-list', listener); + const overviewButton = new LineUpPanelButton(buttons, 'En/Disable Overview', this.options.enableOverviewMode === 'active' ? 'fa fa-th-list' : 'fa fa-list', listener); this.overview = overviewButton.node; // TODO might be removed this.header.addButton(overviewButton); } - } - private createTabMarkup(title: string, linkClass: string, onClick: (ranking: Ranking) => void, extraOptions?) { - const b = this.node.ownerDocument.createElement('a'); - b.className = linkClass; - b.setAttribute('data-toggle', 'tab'); - b.href = '#b'; - b.title = title; - b.addEventListener('click', (evt) => { - evt.stopPropagation(); - evt.preventDefault(); - const openTab = () => { - $('.fa-calculator').tab('show'); - this.collapse = false; - } - const openDefaultTab = () => { - if (this.node.querySelector('#b').classList.contains('active')) { - $('.hello').tab('show'); - return; - } - $('.fa-calculator').tab('show'); - } - this.collapse ? openTab() : openDefaultTab() - const first = this.provider.getRankings()[0]; - if (first) { - onClick(first); - } - }); - return b; + if(!this.isTopMode) { + this.appendExtraTabs(buttons).forEach((button: LineUpPanelButton) => { + this.header.addButton(button); + }); + } } setViolation(violation?: string) { @@ -461,18 +479,41 @@ export default class LineUpPanelActions extends EventHandler { button.load().then((p) => this.scoreColumnDialog(p)); }; - const luButton = new LineUpPanelButton(parent, this.provider, button.title, 'fa ' + button.cssClass, listener); + const luButton = new LineUpPanelRankingButton(parent, this.provider, button.title, 'fa ' + button.cssClass, listener); this.header.addButton(luButton); }); } - private appendExtraTabs() { - const buttons = listPlugins(EXTENSION_POINT_TDP_LINEUP_PANEL_TAB); - return buttons.map((button) => { + private appendExtraTabs(buttons: HTMLElement) { + const plugins = listPlugins(EXTENSION_POINT_TDP_LINEUP_PANEL_TAB); + return plugins.map((plugin) => { + const tab = new LineUpPanelTab(this.tabContainer.node, plugin.tabWidth); + this.tabContainer.addTab(tab); + + let isLoaded = false; + const listener = () => { - button.load().then((p) => p.factory(p.desc, this.node, this.provider)); + if(isLoaded) { + if(this.collapse) { + this.collapse = false; // expand side panel + this.tabContainer.show(tab); + + } else { + this.tabContainer.toggle(tab); + } + + } else { + plugin.load().then((p) => { + p.factory(tab.node, this.provider, p.desc); + + this.collapse = false; // expand side panel + this.tabContainer.show(tab); + + isLoaded = true; + }); + } }; - return this.createTabMarkup(button.title, 'fa ' + button.cssClass, listener); + return new LineUpPanelButton(buttons, plugin.title, 'fa ' + plugin.cssClass, listener); }); } From 930758ff0afd8329bd839f738dd0bcc5925e5afa Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Tue, 17 Dec 2019 09:39:37 +0100 Subject: [PATCH 07/63] adapted panel buttons styles --- src/lineup/internal/LineUpPanelActions.ts | 29 ++++++++++++----------- src/styles/_view_lineup.scss | 13 +++++----- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/lineup/internal/LineUpPanelActions.ts b/src/lineup/internal/LineUpPanelActions.ts index 0fe2d2449..83216dcb9 100644 --- a/src/lineup/internal/LineUpPanelActions.ts +++ b/src/lineup/internal/LineUpPanelActions.ts @@ -11,8 +11,6 @@ import {EventHandler} from 'phovea_core/src/event'; import {IARankingViewOptions} from '../ARankingView'; import {exportLogic} from './export'; import {lazyDialogModule} from '../../dialogs'; -import * as $ from 'jquery'; -import {threadId} from 'worker_threads'; export interface ISearchOption { text: string; @@ -112,6 +110,9 @@ class LineUpPanelButton implements ILineUpPanelButton { onClick(); }); } + public highlight() { + this.node.style.color = 'orange'; + } } class LineUpPanelRankingButton implements ILineUpPanelButton { @@ -238,7 +239,7 @@ class LineUpPanelTabContainer { } toggle(tab: LineUpPanelTab) { - if(this.currentTab === tab) { + if (this.currentTab === tab) { this.hide(tab); } else { @@ -247,7 +248,7 @@ class LineUpPanelTabContainer { } show(tab: LineUpPanelTab) { - if(this.currentTab) { + if (this.currentTab) { this.currentTab.hide(); } @@ -272,9 +273,9 @@ class LineUpPanelTab { readonly node: HTMLElement; - constructor(parent: HTMLElement, options?: Partial) { + constructor(parent: HTMLElement, cssClass: string, options?: Partial) { this.node = parent.ownerDocument.createElement('div'); - this.node.classList.add('tab-pane'); + this.node.classList.add('tab-pane', cssClass); const o = Object.assign({}, options); this.node.style.width = o.width || null; @@ -411,12 +412,12 @@ export default class LineUpPanelActions extends EventHandler { this.collapse = !this.collapse; }; - const collapseButton = new LineUpPanelButton(buttons, '(Un)Collapse', 'collapse-button', listener); + const collapseButton = new LineUpPanelButton(buttons, '(Un)Collapse', 'collapse-button', listener); this.header.addButton(collapseButton); } if (this.options.enableAddingColumns) { - const addColumnButton = new LineUpPanelAddColumnButton(buttons, this.searchBoxProvider.createSearchBox()); + const addColumnButton = new LineUpPanelAddColumnButton(buttons, this.searchBoxProvider.createSearchBox()); this.header.addButton(addColumnButton); } @@ -429,7 +430,7 @@ export default class LineUpPanelActions extends EventHandler { }); }; - const saveRankingButton = new LineUpPanelRankingButton(buttons, this.provider, 'Save List of Entities', 'fa fa-save', listener); + const saveRankingButton = new LineUpPanelRankingButton(buttons, this.provider, 'Save List of Entities', 'fa fa-save', listener); this.header.addButton(saveRankingButton); } @@ -457,7 +458,7 @@ export default class LineUpPanelActions extends EventHandler { this.header.addButton(overviewButton); } - if(!this.isTopMode) { + if (!this.isTopMode) { this.appendExtraTabs(buttons).forEach((button: LineUpPanelButton) => { this.header.addButton(button); }); @@ -479,7 +480,7 @@ export default class LineUpPanelActions extends EventHandler { button.load().then((p) => this.scoreColumnDialog(p)); }; - const luButton = new LineUpPanelRankingButton(parent, this.provider, button.title, 'fa ' + button.cssClass, listener); + const luButton = new LineUpPanelRankingButton(parent, this.provider, button.title, 'fa ' + button.cssClass, listener); this.header.addButton(luButton); }); } @@ -487,14 +488,14 @@ export default class LineUpPanelActions extends EventHandler { private appendExtraTabs(buttons: HTMLElement) { const plugins = listPlugins(EXTENSION_POINT_TDP_LINEUP_PANEL_TAB); return plugins.map((plugin) => { - const tab = new LineUpPanelTab(this.tabContainer.node, plugin.tabWidth); + const tab = new LineUpPanelTab(this.tabContainer.node, 'fa ' + plugin.cssClass, plugin.tabWidth); this.tabContainer.addTab(tab); let isLoaded = false; const listener = () => { - if(isLoaded) { - if(this.collapse) { + if (isLoaded) { + if (this.collapse) { this.collapse = false; // expand side panel this.tabContainer.show(tab); diff --git a/src/styles/_view_lineup.scss b/src/styles/_view_lineup.scss index 15931b59a..d57fd328f 100644 --- a/src/styles/_view_lineup.scss +++ b/src/styles/_view_lineup.scss @@ -119,7 +119,7 @@ $lu_assets: '~lineupjs/src/assets'; display: flex; flex-direction: column; position: relative; - + overflow:hidden; %lu-panel-button { background: #fff; border: 1px solid #ddd; @@ -142,7 +142,9 @@ $lu_assets: '~lineupjs/src/assets'; } >header { - display: flex; + margin-bottom: 1em; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(2.8em, 1fr)); .lu-adder { margin-top: 1em; @@ -175,10 +177,9 @@ $lu_assets: '~lineupjs/src/assets'; .collapse-button { @extend %lu-panel-button; - // width: 2em; + grid-row: 1/3; height: 2em; - // position: absolute; - margin-right: 1em; + margin-right: 0.5em; &::before { @include fa-icon(); @@ -243,9 +244,9 @@ $lu_assets: '~lineupjs/src/assets'; min-width: 0; display: flex; flex-direction: column; + overflow: visible; .collapse-button { - width: 2.5em; position: relative; margin-right: 0; From 630e09988991b93dfb326e6fa1d0491abcee3a5b Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Tue, 17 Dec 2019 10:48:57 +0100 Subject: [PATCH 08/63] further refactoring --- src/lineup/internal/LineUpPanelActions.ts | 279 +----------------- .../panel/LineUpPanelAddColumnButton.ts | 31 ++ .../internal/panel/LineUpPanelButton.ts | 19 ++ .../panel/LineUpPanelDownloadButton.ts | 55 ++++ .../internal/panel/LineUpPanelHeader.ts | 18 ++ src/lineup/internal/panel/LineUpPanelTab.ts | 43 +++ .../internal/panel/LineUpPanelTabContainer.ts | 49 +++ .../internal/panel/LineUpSearchBoxProvider.ts | 33 +++ .../panel/LineupPanelRankingButton.ts | 21 ++ src/styles/_view_lineup.scss | 8 +- 10 files changed, 284 insertions(+), 272 deletions(-) create mode 100644 src/lineup/internal/panel/LineUpPanelAddColumnButton.ts create mode 100644 src/lineup/internal/panel/LineUpPanelButton.ts create mode 100644 src/lineup/internal/panel/LineUpPanelDownloadButton.ts create mode 100644 src/lineup/internal/panel/LineUpPanelHeader.ts create mode 100644 src/lineup/internal/panel/LineUpPanelTab.ts create mode 100644 src/lineup/internal/panel/LineUpPanelTabContainer.ts create mode 100644 src/lineup/internal/panel/LineUpSearchBoxProvider.ts create mode 100644 src/lineup/internal/panel/LineupPanelRankingButton.ts diff --git a/src/lineup/internal/LineUpPanelActions.ts b/src/lineup/internal/LineUpPanelActions.ts index 83216dcb9..6656417c4 100644 --- a/src/lineup/internal/LineUpPanelActions.ts +++ b/src/lineup/internal/LineUpPanelActions.ts @@ -9,8 +9,15 @@ import { } from '../../extensions'; import {EventHandler} from 'phovea_core/src/event'; import {IARankingViewOptions} from '../ARankingView'; -import {exportLogic} from './export'; import {lazyDialogModule} from '../../dialogs'; +import LineUpPanelButton, {ILineUpPanelButton} from './panel/LineUpPanelButton'; +import LineUpPanelTabContainer from './panel/LineUpPanelTabContainer'; +import LineUpPanelDownloadButton from './panel/LineUpPanelDownloadButton'; +import {LineUpPanelTab, LineUpSidePanelTab} from './panel/LineUpPanelTab'; +import LineUpSearchBoxProvider from './panel/LineUpSearchBoxProvider'; +import LineUpPanelHeader from './panel/LineUpPanelHeader'; +import LineUpPanelRankingButton from './panel/LineupPanelRankingButton'; +import LineUpPanelAddColumnButton from './panel/LineUpPanelAddColumnButton'; export interface ISearchOption { text: string; @@ -40,274 +47,6 @@ export function wrap(score: IPluginDesc): IScoreLoader { }; } -class LineUpSearchBoxProvider { - - private searchBoxes: SearchBox[] = []; - - private idType: IDType | null = null; - - constructor(private provider: LocalDataProvider, private options: any) { - - } - - get length(): number { - return this.searchBoxes.length; - } - - createSearchBox(): SearchBox { - const searchBox = new SearchBox({ - placeholder: 'Add Column...' - }); - - searchBox.on(SearchBox.EVENT_SELECT, (item) => { - item.action(); - }); - - this.searchBoxes = [...this.searchBoxes, searchBox]; - - return searchBox; - } - - update(items: (ISearchOption | IGroupSearchItem)[]) { - this.searchBoxes.forEach((searchBox) => searchBox.data = items); - } -} - - -interface ILineUpPanelButton { - readonly node: HTMLElement; -} - -class LineUpPanelHeader { - - readonly node: HTMLElement; - - private buttons: ILineUpPanelButton[] = []; - - constructor(parent: HTMLElement) { - this.node = parent.ownerDocument.createElement('header'); - parent.appendChild(this.node); - } - - addButton(button: ILineUpPanelButton) { - this.buttons = [...this.buttons, button]; - this.node.appendChild(button.node); - } - -} - - -class LineUpPanelButton implements ILineUpPanelButton { - readonly node: HTMLElement; - - constructor(parent: HTMLElement, title: string, linkClass: string, onClick: () => void) { - this.node = parent.ownerDocument.createElement('button'); - this.node.className = linkClass; - this.node.title = title; - this.node.addEventListener('click', (evt) => { - evt.stopPropagation(); - evt.preventDefault(); - onClick(); - }); - } - public highlight() { - this.node.style.color = 'orange'; - } -} - -class LineUpPanelRankingButton implements ILineUpPanelButton { - readonly node: HTMLElement; - - constructor(parent: HTMLElement, private provider: LocalDataProvider, title: string, linkClass: string, onClick: (ranking: Ranking) => void) { - this.node = parent.ownerDocument.createElement('button'); - this.node.className = linkClass; - this.node.title = title; - this.node.addEventListener('click', (evt) => { - evt.stopPropagation(); - evt.preventDefault(); - const first = this.provider.getRankings()[0]; - if (first) { - onClick(first); - } - }); - } -} - -class LineUpPanelDownloadButton implements ILineUpPanelButton { - readonly node: HTMLElement; - - constructor(parent: HTMLElement, private provider: LocalDataProvider, isTopMode: boolean) { - this.node = parent.ownerDocument.createElement('div'); - this.node.classList.add('btn-group', 'download-data-dropdown'); - this.node.innerHTML = ` - - - `; - - // Listen for row selection and update number of selected rows - // Show/hide some dropdown menu points accordingly using CSS - this.provider.on(LocalDataProvider.EVENT_SELECTION_CHANGED + '.download-menu', (indices: number[]) => { - (this.node.querySelector('[data-num-selected-rows]')).dataset.numSelectedRows = indices.length.toString(); - }); - - const links = Array.from(this.node.querySelectorAll('a')); - for (const link of links) { - link.onclick = (evt) => { - evt.preventDefault(); - evt.stopPropagation(); - const type = link.dataset.t; - const onlySelected = link.dataset.s === 's'; - exportLogic(type, onlySelected, this.provider).then(({content, mimeType, name}) => { - this.downloadFile(content, mimeType, name); - }); - }; - } - } - - private downloadFile(content: BufferSource | Blob | string, mimeType: string, name: string) { - const doc = this.node.ownerDocument; - const downloadLink = doc.createElement('a'); - const blob = new Blob([content], {type: mimeType}); - downloadLink.href = URL.createObjectURL(blob); - (downloadLink).download = name; - - doc.body.appendChild(downloadLink); - downloadLink.click(); - downloadLink.remove(); - } - -} - -class LineUpPanelAddColumnButton implements ILineUpPanelButton { - readonly node: HTMLElement; - - constructor(parent: HTMLElement, private readonly search: SearchBox) { - this.node = parent.ownerDocument.createElement('div'); - this.node.classList.add('lu-adder'); - - this.node.addEventListener('mouseleave', () => { - this.node.classList.remove('once'); - }); - - const button = this.node.ownerDocument.createElement('button'); - button.classList.add('fa', 'fa-plus'); - button.title = 'Add Column'; - - button.addEventListener('click', (evt) => { - evt.preventDefault(); - evt.stopPropagation(); - this.node.classList.add('once'); - (this.search.node.querySelector('input'))!.focus(); - this.search.focus(); - }); - - this.node.appendChild(button); - this.node.appendChild(this.search.node); - } -} - - -class LineUpPanelTabContainer { - - readonly node: HTMLElement; - - private tabs: LineUpPanelTab[] = []; - - private currentTab: LineUpPanelTab; - - constructor(parent: HTMLElement) { - this.node = parent.ownerDocument.createElement('main'); - this.node.classList.add('tab-content'); - parent.appendChild(this.node); - } - - private get defaultTab(): LineUpPanelTab { - return this.tabs[0]; - } - - addTab(tab: LineUpPanelTab) { - this.tabs = [...this.tabs, tab]; - this.node.appendChild(tab.node); - } - - toggle(tab: LineUpPanelTab) { - if (this.currentTab === tab) { - this.hide(tab); - - } else { - this.show(tab); - } - } - - show(tab: LineUpPanelTab) { - if (this.currentTab) { - this.currentTab.hide(); - } - - tab.show(); - this.currentTab = tab; - } - - hide(tab: LineUpPanelTab) { - tab.hide(); - this.defaultTab.show(); - this.currentTab = this.defaultTab; - } - -} - - -interface ILineUpPanelTabOptions { - width: string; -} - -class LineUpPanelTab { - - readonly node: HTMLElement; - - constructor(parent: HTMLElement, cssClass: string, options?: Partial) { - this.node = parent.ownerDocument.createElement('div'); - this.node.classList.add('tab-pane', cssClass); - - const o = Object.assign({}, options); - this.node.style.width = o.width || null; - } - - show() { - this.node.classList.add('active'); - } - - hide() { - this.node.classList.remove('active'); - } -} - -class LineUpSidePanelTab extends LineUpPanelTab { - - readonly panel: SidePanel | null; - - constructor(parent: HTMLElement, private readonly search: SearchBox, ctx: any, doc = document) { - super(parent); - - this.panel = new SidePanel(ctx, doc, { - chooser: false - }); - - this.node.appendChild(this.search.node); - this.node.appendChild(this.panel.node); - } - -} - - export default class LineUpPanelActions extends EventHandler { static readonly EVENT_ZOOM_OUT = 'zoomOut'; static readonly EVENT_ZOOM_IN = 'zoomIn'; @@ -488,7 +227,7 @@ export default class LineUpPanelActions extends EventHandler { private appendExtraTabs(buttons: HTMLElement) { const plugins = listPlugins(EXTENSION_POINT_TDP_LINEUP_PANEL_TAB); return plugins.map((plugin) => { - const tab = new LineUpPanelTab(this.tabContainer.node, 'fa ' + plugin.cssClass, plugin.tabWidth); + const tab = new LineUpPanelTab(this.tabContainer.node, plugin.tabWidth); this.tabContainer.addTab(tab); let isLoaded = false; diff --git a/src/lineup/internal/panel/LineUpPanelAddColumnButton.ts b/src/lineup/internal/panel/LineUpPanelAddColumnButton.ts new file mode 100644 index 000000000..8be246edc --- /dev/null +++ b/src/lineup/internal/panel/LineUpPanelAddColumnButton.ts @@ -0,0 +1,31 @@ +import {SearchBox} from 'lineupjs'; +import {ISearchOption} from '../LineUpPanelActions'; +import {ILineUpPanelButton} from './LineUpPanelButton'; + +export default class LineUpPanelAddColumnButton implements ILineUpPanelButton { + readonly node: HTMLElement; + + constructor(parent: HTMLElement, private readonly search: SearchBox) { + this.node = parent.ownerDocument.createElement('div'); + this.node.classList.add('lu-adder'); + + this.node.addEventListener('mouseleave', () => { + this.node.classList.remove('once'); + }); + + const button = this.node.ownerDocument.createElement('button'); + button.classList.add('fa', 'fa-plus'); + button.title = 'Add Column'; + + button.addEventListener('click', (evt) => { + evt.preventDefault(); + evt.stopPropagation(); + this.node.classList.add('once'); + (this.search.node.querySelector('input'))!.focus(); + this.search.focus(); + }); + + this.node.appendChild(button); + this.node.appendChild(this.search.node); + } +} diff --git a/src/lineup/internal/panel/LineUpPanelButton.ts b/src/lineup/internal/panel/LineUpPanelButton.ts new file mode 100644 index 000000000..a72134b90 --- /dev/null +++ b/src/lineup/internal/panel/LineUpPanelButton.ts @@ -0,0 +1,19 @@ + +export interface ILineUpPanelButton { + readonly node: HTMLElement; +} + +export default class LineUpPanelButton implements ILineUpPanelButton { + readonly node: HTMLElement; + + constructor(parent: HTMLElement, title: string, linkClass: string, onClick: () => void) { + this.node = parent.ownerDocument.createElement('button'); + this.node.className = linkClass; + this.node.title = title; + this.node.addEventListener('click', (evt) => { + evt.stopPropagation(); + evt.preventDefault(); + onClick(); + }); + } +} diff --git a/src/lineup/internal/panel/LineUpPanelDownloadButton.ts b/src/lineup/internal/panel/LineUpPanelDownloadButton.ts new file mode 100644 index 000000000..d6086d1e8 --- /dev/null +++ b/src/lineup/internal/panel/LineUpPanelDownloadButton.ts @@ -0,0 +1,55 @@ +import {LocalDataProvider} from 'lineupjs'; +import {exportLogic} from '../export'; +import {ILineUpPanelButton} from './LineUpPanelButton'; + +export default class LineUpPanelDownloadButton implements ILineUpPanelButton { + readonly node: HTMLElement; + + constructor(parent: HTMLElement, private provider: LocalDataProvider, isTopMode: boolean) { + this.node = parent.ownerDocument.createElement('div'); + this.node.classList.add('btn-group', 'download-data-dropdown'); + this.node.innerHTML = ` + + + `; + + // Listen for row selection and update number of selected rows + // Show/hide some dropdown menu points accordingly using CSS + this.provider.on(LocalDataProvider.EVENT_SELECTION_CHANGED + '.download-menu', (indices: number[]) => { + (this.node.querySelector('[data-num-selected-rows]')).dataset.numSelectedRows = indices.length.toString(); + }); + + const links = Array.from(this.node.querySelectorAll('a')); + for (const link of links) { + link.onclick = (evt) => { + evt.preventDefault(); + evt.stopPropagation(); + const type = link.dataset.t; + const onlySelected = link.dataset.s === 's'; + exportLogic(type, onlySelected, this.provider).then(({content, mimeType, name}) => { + this.downloadFile(content, mimeType, name); + }); + }; + } + } + + private downloadFile(content: BufferSource | Blob | string, mimeType: string, name: string) { + const doc = this.node.ownerDocument; + const downloadLink = doc.createElement('a'); + const blob = new Blob([content], {type: mimeType}); + downloadLink.href = URL.createObjectURL(blob); + (downloadLink).download = name; + + doc.body.appendChild(downloadLink); + downloadLink.click(); + downloadLink.remove(); + } +} diff --git a/src/lineup/internal/panel/LineUpPanelHeader.ts b/src/lineup/internal/panel/LineUpPanelHeader.ts new file mode 100644 index 000000000..098ee073a --- /dev/null +++ b/src/lineup/internal/panel/LineUpPanelHeader.ts @@ -0,0 +1,18 @@ +import {ILineUpPanelButton} from './LineUpPanelButton'; + +export default class LineUpPanelHeader { + + readonly node: HTMLElement; + + private buttons: ILineUpPanelButton[] = []; + + constructor(parent: HTMLElement) { + this.node = parent.ownerDocument.createElement('header'); + parent.appendChild(this.node); + } + + addButton(button: ILineUpPanelButton) { + this.buttons = [...this.buttons, button]; + this.node.appendChild(button.node); + } +} diff --git a/src/lineup/internal/panel/LineUpPanelTab.ts b/src/lineup/internal/panel/LineUpPanelTab.ts new file mode 100644 index 000000000..157b41a35 --- /dev/null +++ b/src/lineup/internal/panel/LineUpPanelTab.ts @@ -0,0 +1,43 @@ +import {SidePanel, SearchBox} from 'lineupjs'; +import {ISearchOption} from '../LineUpPanelActions'; + +interface ILineUpPanelTabOptions { + width: string; +} + +export class LineUpPanelTab { + + readonly node: HTMLElement; + + constructor(parent: HTMLElement, options?: Partial) { + this.node = parent.ownerDocument.createElement('div'); + this.node.classList.add('tab-pane'); + + const o = Object.assign({}, options); + this.node.style.width = o.width || null; + } + + show() { + this.node.classList.add('active'); + } + + hide() { + this.node.classList.remove('active'); + } +} + +export class LineUpSidePanelTab extends LineUpPanelTab { + + readonly panel: SidePanel | null; + + constructor(parent: HTMLElement, private readonly search: SearchBox, ctx: any, doc = document) { + super(parent); + + this.panel = new SidePanel(ctx, doc, { + chooser: false + }); + + this.node.appendChild(this.search.node); + this.node.appendChild(this.panel.node); + } +} diff --git a/src/lineup/internal/panel/LineUpPanelTabContainer.ts b/src/lineup/internal/panel/LineUpPanelTabContainer.ts new file mode 100644 index 000000000..608bbac2f --- /dev/null +++ b/src/lineup/internal/panel/LineUpPanelTabContainer.ts @@ -0,0 +1,49 @@ +import {LineUpPanelTab} from './LineUpPanelTab'; + +export default class LineUpPanelTabContainer { + + readonly node: HTMLElement; + + private tabs: LineUpPanelTab[] = []; + + private currentTab: LineUpPanelTab; + + constructor(parent: HTMLElement) { + this.node = parent.ownerDocument.createElement('main'); + this.node.classList.add('tab-content'); + parent.appendChild(this.node); + } + + private get defaultTab(): LineUpPanelTab { + return this.tabs[0]; + } + + addTab(tab: LineUpPanelTab) { + this.tabs = [...this.tabs, tab]; + this.node.appendChild(tab.node); + } + + toggle(tab: LineUpPanelTab) { + if (this.currentTab === tab) { + this.hide(tab); + + } else { + this.show(tab); + } + } + + show(tab: LineUpPanelTab) { + if (this.currentTab) { + this.currentTab.hide(); + } + + tab.show(); + this.currentTab = tab; + } + + hide(tab: LineUpPanelTab) { + tab.hide(); + this.defaultTab.show(); + this.currentTab = this.defaultTab; + } +} diff --git a/src/lineup/internal/panel/LineUpSearchBoxProvider.ts b/src/lineup/internal/panel/LineUpSearchBoxProvider.ts new file mode 100644 index 000000000..9968c9813 --- /dev/null +++ b/src/lineup/internal/panel/LineUpSearchBoxProvider.ts @@ -0,0 +1,33 @@ +import {SearchBox, LocalDataProvider, IGroupSearchItem} from 'lineupjs'; +import {ISearchOption} from '../LineUpPanelActions'; + +export default class LineUpSearchBoxProvider { + + private searchBoxes: SearchBox[] = []; + + constructor(private provider: LocalDataProvider, private options: any) { + + } + + get length(): number { + return this.searchBoxes.length; + } + + createSearchBox(): SearchBox { + const searchBox = new SearchBox({ + placeholder: 'Add Column...' + }); + + searchBox.on(SearchBox.EVENT_SELECT, (item) => { + item.action(); + }); + + this.searchBoxes = [...this.searchBoxes, searchBox]; + + return searchBox; + } + + update(items: (ISearchOption | IGroupSearchItem)[]) { + this.searchBoxes.forEach((searchBox) => searchBox.data = items); + } +} diff --git a/src/lineup/internal/panel/LineupPanelRankingButton.ts b/src/lineup/internal/panel/LineupPanelRankingButton.ts new file mode 100644 index 000000000..7cba612a5 --- /dev/null +++ b/src/lineup/internal/panel/LineupPanelRankingButton.ts @@ -0,0 +1,21 @@ +import {ILineUpPanelButton} from './LineUpPanelButton'; + +import {LocalDataProvider, Ranking} from 'lineupjs'; + +export default class LineUpPanelRankingButton implements ILineUpPanelButton { + readonly node: HTMLElement; + + constructor(parent: HTMLElement, private provider: LocalDataProvider, title: string, linkClass: string, onClick: (ranking: Ranking) => void) { + this.node = parent.ownerDocument.createElement('button'); + this.node.className = linkClass; + this.node.title = title; + this.node.addEventListener('click', (evt) => { + evt.stopPropagation(); + evt.preventDefault(); + const first = this.provider.getRankings()[0]; + if (first) { + onClick(first); + } + }); + } +} diff --git a/src/styles/_view_lineup.scss b/src/styles/_view_lineup.scss index d57fd328f..f0a735b03 100644 --- a/src/styles/_view_lineup.scss +++ b/src/styles/_view_lineup.scss @@ -119,7 +119,8 @@ $lu_assets: '~lineupjs/src/assets'; display: flex; flex-direction: column; position: relative; - overflow:hidden; + overflow: hidden; + %lu-panel-button { background: #fff; border: 1px solid #ddd; @@ -151,12 +152,15 @@ $lu_assets: '~lineupjs/src/assets'; display: none; } - a, + .collapse-button, button { @extend %lu-panel-button; height: 2em; } + button.active { + color: orange; + } } label, From ced69242c2609b049547b7a14ff7589a426b93f5 Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Tue, 17 Dec 2019 12:32:31 +0100 Subject: [PATCH 09/63] restructeded files --- src/lineup/internal/LineUpPanelActions.ts | 52 +++++++++---------- .../internal/panel/LineUpPanelButton.ts | 19 ------- ...olumnButton.ts => PanelAddColumnButton.ts} | 4 +- src/lineup/internal/panel/PanelButton.ts | 43 +++++++++++++++ ...wnloadButton.ts => PanelDownloadButton.ts} | 4 +- .../{LineUpPanelHeader.ts => PanelHeader.ts} | 8 +-- ...RankingButton.ts => PanelRankingButton.ts} | 5 +- .../panel/{LineUpPanelTab.ts => PanelTab.ts} | 8 +-- ...elTabContainer.ts => PanelTabContainer.ts} | 18 +++---- ...rchBoxProvider.ts => SearchBoxProvider.ts} | 2 +- src/styles/_view_lineup.scss | 6 ++- 11 files changed, 98 insertions(+), 71 deletions(-) delete mode 100644 src/lineup/internal/panel/LineUpPanelButton.ts rename src/lineup/internal/panel/{LineUpPanelAddColumnButton.ts => PanelAddColumnButton.ts} (86%) create mode 100644 src/lineup/internal/panel/PanelButton.ts rename src/lineup/internal/panel/{LineUpPanelDownloadButton.ts => PanelDownloadButton.ts} (94%) rename src/lineup/internal/panel/{LineUpPanelHeader.ts => PanelHeader.ts} (58%) rename src/lineup/internal/panel/{LineupPanelRankingButton.ts => PanelRankingButton.ts} (81%) rename src/lineup/internal/panel/{LineUpPanelTab.ts => PanelTab.ts} (79%) rename src/lineup/internal/panel/{LineUpPanelTabContainer.ts => PanelTabContainer.ts} (64%) rename src/lineup/internal/panel/{LineUpSearchBoxProvider.ts => SearchBoxProvider.ts} (94%) diff --git a/src/lineup/internal/LineUpPanelActions.ts b/src/lineup/internal/LineUpPanelActions.ts index 6656417c4..7e9bd3c65 100644 --- a/src/lineup/internal/LineUpPanelActions.ts +++ b/src/lineup/internal/LineUpPanelActions.ts @@ -10,14 +10,14 @@ import { import {EventHandler} from 'phovea_core/src/event'; import {IARankingViewOptions} from '../ARankingView'; import {lazyDialogModule} from '../../dialogs'; -import LineUpPanelButton, {ILineUpPanelButton} from './panel/LineUpPanelButton'; -import LineUpPanelTabContainer from './panel/LineUpPanelTabContainer'; -import LineUpPanelDownloadButton from './panel/LineUpPanelDownloadButton'; -import {LineUpPanelTab, LineUpSidePanelTab} from './panel/LineUpPanelTab'; -import LineUpSearchBoxProvider from './panel/LineUpSearchBoxProvider'; -import LineUpPanelHeader from './panel/LineUpPanelHeader'; -import LineUpPanelRankingButton from './panel/LineupPanelRankingButton'; -import LineUpPanelAddColumnButton from './panel/LineUpPanelAddColumnButton'; +import PanelButton, {IPanelButton, PanelNavButton} from './panel/PanelButton'; +import PanelTabContainer from './panel/PanelTabContainer'; +import PanelDownloadButton from './panel/PanelDownloadButton'; +import {PanelTab, SidePanelTab} from './panel/PanelTab'; +import SearchBoxProvider from './panel/SearchBoxProvider'; +import PanelHeader from './panel/PanelHeader'; +import PanelRankingButton from './panel/PanelRankingButton'; +import PanelAddColumnButton from './panel/PanelAddColumnButton'; export interface ISearchOption { text: string; @@ -64,13 +64,13 @@ export default class LineUpPanelActions extends EventHandler { private idType: IDType | null = null; - private readonly searchBoxProvider: LineUpSearchBoxProvider; + private readonly searchBoxProvider: SearchBoxProvider; readonly panel: SidePanel | null; readonly node: HTMLElement; // wrapper node - private readonly header: LineUpPanelHeader; - private readonly tabContainer: LineUpPanelTabContainer; + private readonly header: PanelHeader; + private readonly tabContainer: PanelTabContainer; private overview: HTMLElement; private wasCollapsed = false; @@ -81,9 +81,9 @@ export default class LineUpPanelActions extends EventHandler { this.node = doc.createElement('aside'); this.node.classList.add('lu-side-panel-wrapper'); - this.header = new LineUpPanelHeader(this.node); + this.header = new PanelHeader(this.node); - this.searchBoxProvider = new LineUpSearchBoxProvider(provider, options); + this.searchBoxProvider = new SearchBoxProvider(provider, options); // this.options.enableSidePanel = 'top'; @@ -91,10 +91,10 @@ export default class LineUpPanelActions extends EventHandler { this.node.classList.add('lu-side-panel-top'); } else { - const sidePanel = new LineUpSidePanelTab(this.node, this.searchBoxProvider.createSearchBox(), ctx, doc); + const sidePanel = new SidePanelTab(this.node, this.searchBoxProvider.createSearchBox(), ctx, doc); this.panel = sidePanel.panel; - this.tabContainer = new LineUpPanelTabContainer(this.node); + this.tabContainer = new PanelTabContainer(this.node); this.tabContainer.addTab(sidePanel); this.tabContainer.show(sidePanel); } @@ -151,12 +151,12 @@ export default class LineUpPanelActions extends EventHandler { this.collapse = !this.collapse; }; - const collapseButton = new LineUpPanelButton(buttons, '(Un)Collapse', 'collapse-button', listener); + const collapseButton = new PanelButton(buttons, '(Un)Collapse', 'collapse-button', listener); this.header.addButton(collapseButton); } if (this.options.enableAddingColumns) { - const addColumnButton = new LineUpPanelAddColumnButton(buttons, this.searchBoxProvider.createSearchBox()); + const addColumnButton = new PanelAddColumnButton(buttons, this.searchBoxProvider.createSearchBox()); this.header.addButton(addColumnButton); } @@ -169,20 +169,20 @@ export default class LineUpPanelActions extends EventHandler { }); }; - const saveRankingButton = new LineUpPanelRankingButton(buttons, this.provider, 'Save List of Entities', 'fa fa-save', listener); + const saveRankingButton = new PanelRankingButton(buttons, this.provider, 'Save List of Entities', 'fa fa-save', listener); this.header.addButton(saveRankingButton); } if (this.options.enableDownload) { - const downloadButton = new LineUpPanelDownloadButton(buttons, this.provider, this.isTopMode); + const downloadButton = new PanelDownloadButton(buttons, this.provider, this.isTopMode); this.header.addButton(downloadButton); } if (this.options.enableZoom) { - const zoomInButton = new LineUpPanelButton(buttons, 'Zoom In', 'fa fa-search-plus gap', () => this.fire(LineUpPanelActions.EVENT_ZOOM_IN)); + const zoomInButton = new PanelButton(buttons, 'Zoom In', 'fa fa-search-plus gap', () => this.fire(LineUpPanelActions.EVENT_ZOOM_IN)); this.header.addButton(zoomInButton); - const zoomOutButton = new LineUpPanelButton(buttons, 'Zoom Out', 'fa fa-search-minus', () => this.fire(LineUpPanelActions.EVENT_ZOOM_OUT)); + const zoomOutButton = new PanelButton(buttons, 'Zoom Out', 'fa fa-search-minus', () => this.fire(LineUpPanelActions.EVENT_ZOOM_OUT)); this.header.addButton(zoomOutButton); } @@ -192,13 +192,13 @@ export default class LineUpPanelActions extends EventHandler { this.overview.classList.toggle('fa-list'); this.fire(LineUpPanelActions.EVENT_RULE_CHANGED, selected ? rule : null); }; - const overviewButton = new LineUpPanelButton(buttons, 'En/Disable Overview', this.options.enableOverviewMode === 'active' ? 'fa fa-th-list' : 'fa fa-list', listener); + const overviewButton = new PanelButton(buttons, 'En/Disable Overview', this.options.enableOverviewMode === 'active' ? 'fa fa-th-list' : 'fa fa-list', listener); this.overview = overviewButton.node; // TODO might be removed this.header.addButton(overviewButton); } if (!this.isTopMode) { - this.appendExtraTabs(buttons).forEach((button: LineUpPanelButton) => { + this.appendExtraTabs(buttons).forEach((button: PanelButton) => { this.header.addButton(button); }); } @@ -219,7 +219,7 @@ export default class LineUpPanelActions extends EventHandler { button.load().then((p) => this.scoreColumnDialog(p)); }; - const luButton = new LineUpPanelRankingButton(parent, this.provider, button.title, 'fa ' + button.cssClass, listener); + const luButton = new PanelRankingButton(parent, this.provider, button.title, 'fa ' + button.cssClass, listener); this.header.addButton(luButton); }); } @@ -227,7 +227,7 @@ export default class LineUpPanelActions extends EventHandler { private appendExtraTabs(buttons: HTMLElement) { const plugins = listPlugins(EXTENSION_POINT_TDP_LINEUP_PANEL_TAB); return plugins.map((plugin) => { - const tab = new LineUpPanelTab(this.tabContainer.node, plugin.tabWidth); + const tab = new PanelTab(this.tabContainer.node, plugin.tabWidth); this.tabContainer.addTab(tab); let isLoaded = false; @@ -253,7 +253,7 @@ export default class LineUpPanelActions extends EventHandler { }); } }; - return new LineUpPanelButton(buttons, plugin.title, 'fa ' + plugin.cssClass, listener); + return new PanelNavButton(buttons, tab.node, plugin.title, 'fa ' + plugin.cssClass, listener); }); } diff --git a/src/lineup/internal/panel/LineUpPanelButton.ts b/src/lineup/internal/panel/LineUpPanelButton.ts deleted file mode 100644 index a72134b90..000000000 --- a/src/lineup/internal/panel/LineUpPanelButton.ts +++ /dev/null @@ -1,19 +0,0 @@ - -export interface ILineUpPanelButton { - readonly node: HTMLElement; -} - -export default class LineUpPanelButton implements ILineUpPanelButton { - readonly node: HTMLElement; - - constructor(parent: HTMLElement, title: string, linkClass: string, onClick: () => void) { - this.node = parent.ownerDocument.createElement('button'); - this.node.className = linkClass; - this.node.title = title; - this.node.addEventListener('click', (evt) => { - evt.stopPropagation(); - evt.preventDefault(); - onClick(); - }); - } -} diff --git a/src/lineup/internal/panel/LineUpPanelAddColumnButton.ts b/src/lineup/internal/panel/PanelAddColumnButton.ts similarity index 86% rename from src/lineup/internal/panel/LineUpPanelAddColumnButton.ts rename to src/lineup/internal/panel/PanelAddColumnButton.ts index 8be246edc..23c21bd67 100644 --- a/src/lineup/internal/panel/LineUpPanelAddColumnButton.ts +++ b/src/lineup/internal/panel/PanelAddColumnButton.ts @@ -1,8 +1,8 @@ import {SearchBox} from 'lineupjs'; import {ISearchOption} from '../LineUpPanelActions'; -import {ILineUpPanelButton} from './LineUpPanelButton'; +import {IPanelButton} from './PanelButton'; -export default class LineUpPanelAddColumnButton implements ILineUpPanelButton { +export default class PanelAddColumnButton implements IPanelButton { readonly node: HTMLElement; constructor(parent: HTMLElement, private readonly search: SearchBox) { diff --git a/src/lineup/internal/panel/PanelButton.ts b/src/lineup/internal/panel/PanelButton.ts new file mode 100644 index 000000000..791c182a5 --- /dev/null +++ b/src/lineup/internal/panel/PanelButton.ts @@ -0,0 +1,43 @@ + +export interface IPanelButton { + readonly node: HTMLElement; +} + +export default class PanelButton implements IPanelButton { + readonly node: HTMLElement; + + constructor(parent: HTMLElement, title: string, linkClass: string, onClick: () => void) { + this.node = parent.ownerDocument.createElement('button'); + this.node.className = linkClass; + this.node.title = title; + this.node.addEventListener('click', (evt) => { + evt.stopPropagation(); + evt.preventDefault(); + onClick(); + }); + } +} +export class PanelNavButton implements IPanelButton { + readonly node: HTMLElement; + + constructor(parent: HTMLElement, private readonly tabNode: HTMLElement, title: string, linkClass: string, onClick: () => void) { + this.node = parent.ownerDocument.createElement('button'); + this.node.className = linkClass; + this.node.title = title; + this.node.addEventListener('click', (evt) => { + evt.stopPropagation(); + evt.preventDefault(); + this.highlight(); + onClick(); + }); + } + private highlight() { + if (!this.tabNode.classList.contains('active')) { + this.node.classList.add('active'); + + } else { + this.node.classList.remove('active'); + } + + } +} diff --git a/src/lineup/internal/panel/LineUpPanelDownloadButton.ts b/src/lineup/internal/panel/PanelDownloadButton.ts similarity index 94% rename from src/lineup/internal/panel/LineUpPanelDownloadButton.ts rename to src/lineup/internal/panel/PanelDownloadButton.ts index d6086d1e8..5e9507e28 100644 --- a/src/lineup/internal/panel/LineUpPanelDownloadButton.ts +++ b/src/lineup/internal/panel/PanelDownloadButton.ts @@ -1,8 +1,8 @@ import {LocalDataProvider} from 'lineupjs'; import {exportLogic} from '../export'; -import {ILineUpPanelButton} from './LineUpPanelButton'; +import {IPanelButton} from './PanelButton'; -export default class LineUpPanelDownloadButton implements ILineUpPanelButton { +export default class PanelDownloadButton implements IPanelButton { readonly node: HTMLElement; constructor(parent: HTMLElement, private provider: LocalDataProvider, isTopMode: boolean) { diff --git a/src/lineup/internal/panel/LineUpPanelHeader.ts b/src/lineup/internal/panel/PanelHeader.ts similarity index 58% rename from src/lineup/internal/panel/LineUpPanelHeader.ts rename to src/lineup/internal/panel/PanelHeader.ts index 098ee073a..943f38c7f 100644 --- a/src/lineup/internal/panel/LineUpPanelHeader.ts +++ b/src/lineup/internal/panel/PanelHeader.ts @@ -1,17 +1,17 @@ -import {ILineUpPanelButton} from './LineUpPanelButton'; +import {IPanelButton} from './PanelButton'; -export default class LineUpPanelHeader { +export default class PanelHeader { readonly node: HTMLElement; - private buttons: ILineUpPanelButton[] = []; + private buttons: IPanelButton[] = []; constructor(parent: HTMLElement) { this.node = parent.ownerDocument.createElement('header'); parent.appendChild(this.node); } - addButton(button: ILineUpPanelButton) { + addButton(button: IPanelButton) { this.buttons = [...this.buttons, button]; this.node.appendChild(button.node); } diff --git a/src/lineup/internal/panel/LineupPanelRankingButton.ts b/src/lineup/internal/panel/PanelRankingButton.ts similarity index 81% rename from src/lineup/internal/panel/LineupPanelRankingButton.ts rename to src/lineup/internal/panel/PanelRankingButton.ts index 7cba612a5..33839c858 100644 --- a/src/lineup/internal/panel/LineupPanelRankingButton.ts +++ b/src/lineup/internal/panel/PanelRankingButton.ts @@ -1,8 +1,7 @@ -import {ILineUpPanelButton} from './LineUpPanelButton'; - +import {IPanelButton} from './PanelButton'; import {LocalDataProvider, Ranking} from 'lineupjs'; -export default class LineUpPanelRankingButton implements ILineUpPanelButton { +export default class PanelRankingButton implements IPanelButton { readonly node: HTMLElement; constructor(parent: HTMLElement, private provider: LocalDataProvider, title: string, linkClass: string, onClick: (ranking: Ranking) => void) { diff --git a/src/lineup/internal/panel/LineUpPanelTab.ts b/src/lineup/internal/panel/PanelTab.ts similarity index 79% rename from src/lineup/internal/panel/LineUpPanelTab.ts rename to src/lineup/internal/panel/PanelTab.ts index 157b41a35..2aaaa18e3 100644 --- a/src/lineup/internal/panel/LineUpPanelTab.ts +++ b/src/lineup/internal/panel/PanelTab.ts @@ -1,15 +1,15 @@ import {SidePanel, SearchBox} from 'lineupjs'; import {ISearchOption} from '../LineUpPanelActions'; -interface ILineUpPanelTabOptions { +interface IPanelTabOptions { width: string; } -export class LineUpPanelTab { +export class PanelTab { readonly node: HTMLElement; - constructor(parent: HTMLElement, options?: Partial) { + constructor(parent: HTMLElement, options?: Partial) { this.node = parent.ownerDocument.createElement('div'); this.node.classList.add('tab-pane'); @@ -26,7 +26,7 @@ export class LineUpPanelTab { } } -export class LineUpSidePanelTab extends LineUpPanelTab { +export class SidePanelTab extends PanelTab { readonly panel: SidePanel | null; diff --git a/src/lineup/internal/panel/LineUpPanelTabContainer.ts b/src/lineup/internal/panel/PanelTabContainer.ts similarity index 64% rename from src/lineup/internal/panel/LineUpPanelTabContainer.ts rename to src/lineup/internal/panel/PanelTabContainer.ts index 608bbac2f..59e12eaaa 100644 --- a/src/lineup/internal/panel/LineUpPanelTabContainer.ts +++ b/src/lineup/internal/panel/PanelTabContainer.ts @@ -1,12 +1,12 @@ -import {LineUpPanelTab} from './LineUpPanelTab'; +import {PanelTab} from './PanelTab'; -export default class LineUpPanelTabContainer { +export default class PanelTabContainer { readonly node: HTMLElement; - private tabs: LineUpPanelTab[] = []; + private tabs: PanelTab[] = []; - private currentTab: LineUpPanelTab; + private currentTab: PanelTab; constructor(parent: HTMLElement) { this.node = parent.ownerDocument.createElement('main'); @@ -14,16 +14,16 @@ export default class LineUpPanelTabContainer { parent.appendChild(this.node); } - private get defaultTab(): LineUpPanelTab { + private get defaultTab(): PanelTab { return this.tabs[0]; } - addTab(tab: LineUpPanelTab) { + addTab(tab: PanelTab) { this.tabs = [...this.tabs, tab]; this.node.appendChild(tab.node); } - toggle(tab: LineUpPanelTab) { + toggle(tab: PanelTab) { if (this.currentTab === tab) { this.hide(tab); @@ -32,7 +32,7 @@ export default class LineUpPanelTabContainer { } } - show(tab: LineUpPanelTab) { + show(tab: PanelTab) { if (this.currentTab) { this.currentTab.hide(); } @@ -41,7 +41,7 @@ export default class LineUpPanelTabContainer { this.currentTab = tab; } - hide(tab: LineUpPanelTab) { + hide(tab: PanelTab) { tab.hide(); this.defaultTab.show(); this.currentTab = this.defaultTab; diff --git a/src/lineup/internal/panel/LineUpSearchBoxProvider.ts b/src/lineup/internal/panel/SearchBoxProvider.ts similarity index 94% rename from src/lineup/internal/panel/LineUpSearchBoxProvider.ts rename to src/lineup/internal/panel/SearchBoxProvider.ts index 9968c9813..f0cdf09b6 100644 --- a/src/lineup/internal/panel/LineUpSearchBoxProvider.ts +++ b/src/lineup/internal/panel/SearchBoxProvider.ts @@ -1,7 +1,7 @@ import {SearchBox, LocalDataProvider, IGroupSearchItem} from 'lineupjs'; import {ISearchOption} from '../LineUpPanelActions'; -export default class LineUpSearchBoxProvider { +export default class SearchBoxProvider { private searchBoxes: SearchBox[] = []; diff --git a/src/styles/_view_lineup.scss b/src/styles/_view_lineup.scss index f0a735b03..6ce471680 100644 --- a/src/styles/_view_lineup.scss +++ b/src/styles/_view_lineup.scss @@ -159,7 +159,7 @@ $lu_assets: '~lineupjs/src/assets'; } button.active { - color: orange; + color: #ffa500 } } @@ -265,6 +265,10 @@ $lu_assets: '~lineupjs/src/assets'; margin-right: 0; min-width: 0; align-self: flex-start; + + button.active { + color: unset; + } } .tab-content { From e7ee7114eb65838e7806f30442cfaed7cdef9ba8 Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Tue, 17 Dec 2019 14:14:44 +0100 Subject: [PATCH 10/63] minor improvements --- src/lineup/internal/LineUpPanelActions.ts | 2 +- src/lineup/internal/panel/PanelTab.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lineup/internal/LineUpPanelActions.ts b/src/lineup/internal/LineUpPanelActions.ts index 7e9bd3c65..6381272cc 100644 --- a/src/lineup/internal/LineUpPanelActions.ts +++ b/src/lineup/internal/LineUpPanelActions.ts @@ -227,7 +227,7 @@ export default class LineUpPanelActions extends EventHandler { private appendExtraTabs(buttons: HTMLElement) { const plugins = listPlugins(EXTENSION_POINT_TDP_LINEUP_PANEL_TAB); return plugins.map((plugin) => { - const tab = new PanelTab(this.tabContainer.node, plugin.tabWidth); + const tab = new PanelTab(this.tabContainer.node, plugin.options); this.tabContainer.addTab(tab); let isLoaded = false; diff --git a/src/lineup/internal/panel/PanelTab.ts b/src/lineup/internal/panel/PanelTab.ts index 2aaaa18e3..f62ed3733 100644 --- a/src/lineup/internal/panel/PanelTab.ts +++ b/src/lineup/internal/panel/PanelTab.ts @@ -2,7 +2,7 @@ import {SidePanel, SearchBox} from 'lineupjs'; import {ISearchOption} from '../LineUpPanelActions'; interface IPanelTabOptions { - width: string; + tabWidth: string; } export class PanelTab { @@ -14,7 +14,7 @@ export class PanelTab { this.node.classList.add('tab-pane'); const o = Object.assign({}, options); - this.node.style.width = o.width || null; + this.node.style.width = o.tabWidth || null; } show() { From 1e332fed00bc559ccda44e177a7282e1bafcdcba Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Tue, 17 Dec 2019 16:15:28 +0100 Subject: [PATCH 11/63] imporve styles --- src/styles/_view_lineup.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/styles/_view_lineup.scss b/src/styles/_view_lineup.scss index 6ce471680..98f81993c 100644 --- a/src/styles/_view_lineup.scss +++ b/src/styles/_view_lineup.scss @@ -119,7 +119,8 @@ $lu_assets: '~lineupjs/src/assets'; display: flex; flex-direction: column; position: relative; - overflow: hidden; + overflow-x: hidden; + overflow-y: scroll; %lu-panel-button { background: #fff; From f34ecda9235d173b15c446f11729db822ec133d5 Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Thu, 19 Dec 2019 11:12:20 +0100 Subject: [PATCH 12/63] improving styles --- src/styles/_view_lineup.scss | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/styles/_view_lineup.scss b/src/styles/_view_lineup.scss index 98f81993c..17798359a 100644 --- a/src/styles/_view_lineup.scss +++ b/src/styles/_view_lineup.scss @@ -118,9 +118,6 @@ $lu_assets: '~lineupjs/src/assets'; .lu-side-panel-wrapper { display: flex; flex-direction: column; - position: relative; - overflow-x: hidden; - overflow-y: scroll; %lu-panel-button { background: #fff; @@ -180,6 +177,11 @@ $lu_assets: '~lineupjs/src/assets'; } } + .tab-content { + display: flex; + flex-direction: column; + } + .collapse-button { @extend %lu-panel-button; grid-row: 1/3; From 7798f7790d5c59b5d18364c1f15151f8dbcd7507 Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Thu, 19 Dec 2019 14:46:28 +0100 Subject: [PATCH 13/63] minor changes --- .../internal/panel/PanelAddColumnButton.ts | 4 ++-- .../internal/panel/PanelDownloadButton.ts | 24 +++++++++---------- .../internal/panel/SearchBoxProvider.ts | 4 ++-- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/lineup/internal/panel/PanelAddColumnButton.ts b/src/lineup/internal/panel/PanelAddColumnButton.ts index 23c21bd67..adaa1d2ff 100644 --- a/src/lineup/internal/panel/PanelAddColumnButton.ts +++ b/src/lineup/internal/panel/PanelAddColumnButton.ts @@ -1,7 +1,7 @@ import {SearchBox} from 'lineupjs'; import {ISearchOption} from '../LineUpPanelActions'; import {IPanelButton} from './PanelButton'; - +import i18n from 'phovea_core/src/i18n'; export default class PanelAddColumnButton implements IPanelButton { readonly node: HTMLElement; @@ -15,7 +15,7 @@ export default class PanelAddColumnButton implements IPanelButton { const button = this.node.ownerDocument.createElement('button'); button.classList.add('fa', 'fa-plus'); - button.title = 'Add Column'; + button.title = i18n.t('tdp:core.lineup.LineupPanelActions.addColumnButton'); button.addEventListener('click', (evt) => { evt.preventDefault(); diff --git a/src/lineup/internal/panel/PanelDownloadButton.ts b/src/lineup/internal/panel/PanelDownloadButton.ts index 5e9507e28..348634350 100644 --- a/src/lineup/internal/panel/PanelDownloadButton.ts +++ b/src/lineup/internal/panel/PanelDownloadButton.ts @@ -1,7 +1,7 @@ import {LocalDataProvider} from 'lineupjs'; import {exportLogic} from '../export'; import {IPanelButton} from './PanelButton'; - +import i18n from 'phovea_core/src/i18n'; export default class PanelDownloadButton implements IPanelButton { readonly node: HTMLElement; @@ -9,17 +9,17 @@ export default class PanelDownloadButton implements IPanelButton { this.node = parent.ownerDocument.createElement('div'); this.node.classList.add('btn-group', 'download-data-dropdown'); this.node.innerHTML = ` - - - `; + + + `; // Listen for row selection and update number of selected rows // Show/hide some dropdown menu points accordingly using CSS diff --git a/src/lineup/internal/panel/SearchBoxProvider.ts b/src/lineup/internal/panel/SearchBoxProvider.ts index f0cdf09b6..840eeecb0 100644 --- a/src/lineup/internal/panel/SearchBoxProvider.ts +++ b/src/lineup/internal/panel/SearchBoxProvider.ts @@ -1,6 +1,6 @@ import {SearchBox, LocalDataProvider, IGroupSearchItem} from 'lineupjs'; import {ISearchOption} from '../LineUpPanelActions'; - +import i18n from 'phovea_core/src/i18n'; export default class SearchBoxProvider { private searchBoxes: SearchBox[] = []; @@ -15,7 +15,7 @@ export default class SearchBoxProvider { createSearchBox(): SearchBox { const searchBox = new SearchBox({ - placeholder: 'Add Column...' + placeholder: i18n.t('tdp:core.lineup.LineupPanelActions.searchPlaceholder') }); searchBox.on(SearchBox.EVENT_SELECT, (item) => { From 5576283bfd9ca13a39c0623d984dd67f2b4c7874 Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Thu, 19 Dec 2019 16:58:46 +0100 Subject: [PATCH 14/63] fixed tab toggling feature --- src/lineup/internal/LineUpPanelActions.ts | 9 ++++++++- src/lineup/internal/panel/PanelHeader.ts | 8 ++++++++ src/lineup/internal/panel/PanelTab.ts | 2 +- src/lineup/internal/panel/PanelTabContainer.ts | 7 +++++++ 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/lineup/internal/LineUpPanelActions.ts b/src/lineup/internal/LineUpPanelActions.ts index 78e8feee9..e9a910fea 100644 --- a/src/lineup/internal/LineUpPanelActions.ts +++ b/src/lineup/internal/LineUpPanelActions.ts @@ -104,6 +104,10 @@ export default class LineUpPanelActions extends EventHandler { this.collapse = options.enableSidePanel === 'top' || options.enableSidePanel === 'collapsed'; } + removeButtonHighlighting() { + this.tabContainer.showDefault(); + this.header.removeHighlighting(); + } forceCollapse() { this.wasCollapsed = this.collapse; @@ -126,6 +130,10 @@ export default class LineUpPanelActions extends EventHandler { set collapse(value: boolean) { this.node.classList.toggle('collapsed', value); + if (value) { + this.removeButtonHighlighting(); + } + } hide() { @@ -246,7 +254,6 @@ export default class LineUpPanelActions extends EventHandler { } else { plugin.load().then((p) => { p.factory(tab.node, this.provider, p.desc); - this.collapse = false; // expand side panel this.tabContainer.show(tab); diff --git a/src/lineup/internal/panel/PanelHeader.ts b/src/lineup/internal/panel/PanelHeader.ts index 943f38c7f..6ca56c7e3 100644 --- a/src/lineup/internal/panel/PanelHeader.ts +++ b/src/lineup/internal/panel/PanelHeader.ts @@ -1,5 +1,6 @@ import {IPanelButton} from './PanelButton'; + export default class PanelHeader { readonly node: HTMLElement; @@ -15,4 +16,11 @@ export default class PanelHeader { this.buttons = [...this.buttons, button]; this.node.appendChild(button.node); } + + removeHighlighting() { + const highlightedButton = this.buttons.find((button) => button.node.classList.contains('active')); + if (highlightedButton) { + highlightedButton.node.classList.remove('active'); + } + } } diff --git a/src/lineup/internal/panel/PanelTab.ts b/src/lineup/internal/panel/PanelTab.ts index f62ed3733..93010b70f 100644 --- a/src/lineup/internal/panel/PanelTab.ts +++ b/src/lineup/internal/panel/PanelTab.ts @@ -32,7 +32,7 @@ export class SidePanelTab extends PanelTab { constructor(parent: HTMLElement, private readonly search: SearchBox, ctx: any, doc = document) { super(parent); - + this.node.classList.add('default'); this.panel = new SidePanel(ctx, doc, { chooser: false }); diff --git a/src/lineup/internal/panel/PanelTabContainer.ts b/src/lineup/internal/panel/PanelTabContainer.ts index 59e12eaaa..bdbdc8a0b 100644 --- a/src/lineup/internal/panel/PanelTabContainer.ts +++ b/src/lineup/internal/panel/PanelTabContainer.ts @@ -32,6 +32,13 @@ export default class PanelTabContainer { } } + showDefault() { + const openedTab = this.tabs.find((tab) => tab.node.classList.contains('tab-pane') && tab.node.classList.contains('active')); + if (openedTab) { + this.hide(openedTab); + } + } + show(tab: PanelTab) { if (this.currentTab) { this.currentTab.hide(); From f2a66b931e0c7309cfa0acf063f54d805a275b7e Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Fri, 20 Dec 2019 08:07:41 +0100 Subject: [PATCH 15/63] extracted util function --- src/lineup/internal/utils.ts | 12 ------------ src/styles/_view_lineup.scss | 10 +--------- 2 files changed, 1 insertion(+), 21 deletions(-) diff --git a/src/lineup/internal/utils.ts b/src/lineup/internal/utils.ts index d2c42ebd3..1e7a267fa 100644 --- a/src/lineup/internal/utils.ts +++ b/src/lineup/internal/utils.ts @@ -9,18 +9,6 @@ import {encodeParams} from 'phovea_core/src/ajax'; -/** - * Checks wether the given function of type IAccessorFunc, i.e. of an AScoreAccessorProxy. - * Beware: coding horrors await beyond this function header. - * @param accessor - */ -export function isProxyAccessor(accessor: any): accessor is IAccessorFunc { - if (accessor && typeof (accessor) === 'function' && accessor.length === 1) { - return accessor.toString() === '(row) => this.access(row.v)'; - } - return false; -} - export interface IAccessorFunc { (row: IDataRow): T; } diff --git a/src/styles/_view_lineup.scss b/src/styles/_view_lineup.scss index 17798359a..990dce854 100644 --- a/src/styles/_view_lineup.scss +++ b/src/styles/_view_lineup.scss @@ -243,9 +243,6 @@ $lu_assets: '~lineupjs/src/assets'; } } - //////////////////// - //////////////////// - &.collapsed { max-width: 3em; min-width: 0; @@ -321,11 +318,6 @@ $lu_assets: '~lineupjs/src/assets'; } } - ////////////////////////////////////////////////// - ////////////////////////////////////////////////// - - - .lu-side-panel-top-form { display: flex; @@ -353,7 +345,7 @@ $lu_assets: '~lineupjs/src/assets'; margin-left: 1em; } - >div.lu-adder { + div.lu-adder { margin-top: 0; &.once .lu-search { From c8d321b4b845ba67e0f0817a37d5f7718c45d1e7 Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Fri, 20 Dec 2019 09:17:02 +0100 Subject: [PATCH 16/63] addapted styles for top mode --- src/lineup/internal/LineUpPanelActions.ts | 8 +++----- src/styles/_view_lineup.scss | 3 +++ 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/lineup/internal/LineUpPanelActions.ts b/src/lineup/internal/LineUpPanelActions.ts index e9a910fea..e4a454820 100644 --- a/src/lineup/internal/LineUpPanelActions.ts +++ b/src/lineup/internal/LineUpPanelActions.ts @@ -86,8 +86,6 @@ export default class LineUpPanelActions extends EventHandler { this.searchBoxProvider = new SearchBoxProvider(provider, options); - // this.options.enableSidePanel = 'top'; - if (this.options.enableSidePanel === 'top') { this.node.classList.add('lu-side-panel-top'); @@ -104,7 +102,7 @@ export default class LineUpPanelActions extends EventHandler { this.collapse = options.enableSidePanel === 'top' || options.enableSidePanel === 'collapsed'; } - removeButtonHighlighting() { + removeTabButtonHighlighting() { this.tabContainer.showDefault(); this.header.removeHighlighting(); } @@ -130,8 +128,8 @@ export default class LineUpPanelActions extends EventHandler { set collapse(value: boolean) { this.node.classList.toggle('collapsed', value); - if (value) { - this.removeButtonHighlighting(); + if (value && this.options.enableSidePanel !== 'top') { + this.removeTabButtonHighlighting(); } } diff --git a/src/styles/_view_lineup.scss b/src/styles/_view_lineup.scss index 990dce854..979c1ddd8 100644 --- a/src/styles/_view_lineup.scss +++ b/src/styles/_view_lineup.scss @@ -334,10 +334,13 @@ $lu_assets: '~lineupjs/src/assets'; max-width: unset; width: unset; float: right; + border-left: none; header { + display: flex; flex-direction: row; max-width: unset; + margin-bottom: 0; } .gap { From 4c122e909ffd75c275dd4a20a5437ef66acc5bec Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Tue, 7 Jan 2020 12:46:14 +0100 Subject: [PATCH 17/63] add hooks to panel tab when panel tab closes --- src/lineup/internal/LineUpPanelActions.ts | 12 ++++++----- src/lineup/internal/panel/PanelTab.ts | 16 ++++++++++++++- .../internal/panel/PanelTabContainer.ts | 20 +++++++++++++------ 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/src/lineup/internal/LineUpPanelActions.ts b/src/lineup/internal/LineUpPanelActions.ts index e4a454820..a91caadfd 100644 --- a/src/lineup/internal/LineUpPanelActions.ts +++ b/src/lineup/internal/LineUpPanelActions.ts @@ -95,7 +95,7 @@ export default class LineUpPanelActions extends EventHandler { this.tabContainer = new PanelTabContainer(this.node); this.tabContainer.addTab(sidePanel); - this.tabContainer.show(sidePanel); + this.tabContainer.showTab(sidePanel); } this.init(); @@ -136,10 +136,12 @@ export default class LineUpPanelActions extends EventHandler { hide() { this.node.style.display = 'none'; + this.tabContainer.hideCurrentTab(); //inform the tab content that the sidepanel is collapsed } show() { this.node.style.display = 'flex'; + this.tabContainer.showCurrentTab();//inform the tab content that the sidepanel is collapsed } private get isTopMode() { @@ -243,17 +245,17 @@ export default class LineUpPanelActions extends EventHandler { if (isLoaded) { if (this.collapse) { this.collapse = false; // expand side panel - this.tabContainer.show(tab); + this.tabContainer.showTab(tab); } else { - this.tabContainer.toggle(tab); + this.tabContainer.toggleTab(tab); } } else { plugin.load().then((p) => { - p.factory(tab.node, this.provider, p.desc); + p.factory(tab.node, this.provider, p.desc, tab.events); this.collapse = false; // expand side panel - this.tabContainer.show(tab); + this.tabContainer.showTab(tab); isLoaded = true; }); diff --git a/src/lineup/internal/panel/PanelTab.ts b/src/lineup/internal/panel/PanelTab.ts index 93010b70f..1522b26bf 100644 --- a/src/lineup/internal/panel/PanelTab.ts +++ b/src/lineup/internal/panel/PanelTab.ts @@ -1,15 +1,27 @@ import {SidePanel, SearchBox} from 'lineupjs'; import {ISearchOption} from '../LineUpPanelActions'; +import {EventHandler} from 'phovea_core/src/event'; -interface IPanelTabOptions { +export interface IPanelTabOptions { tabWidth: string; } +export class PanelTabEvents extends EventHandler { + + static readonly SHOW_PANEL = 'showPanel'; + + static readonly HIDE_PANEL = 'hidePanel'; + +} + export class PanelTab { readonly node: HTMLElement; + readonly events: PanelTabEvents; constructor(parent: HTMLElement, options?: Partial) { + this.events = new PanelTabEvents(); + this.node = parent.ownerDocument.createElement('div'); this.node.classList.add('tab-pane'); @@ -19,10 +31,12 @@ export class PanelTab { show() { this.node.classList.add('active'); + this.events.fire(PanelTabEvents.SHOW_PANEL); } hide() { this.node.classList.remove('active'); + this.events.fire(PanelTabEvents.HIDE_PANEL); } } diff --git a/src/lineup/internal/panel/PanelTabContainer.ts b/src/lineup/internal/panel/PanelTabContainer.ts index bdbdc8a0b..1ddc5c5b1 100644 --- a/src/lineup/internal/panel/PanelTabContainer.ts +++ b/src/lineup/internal/panel/PanelTabContainer.ts @@ -23,23 +23,23 @@ export default class PanelTabContainer { this.node.appendChild(tab.node); } - toggle(tab: PanelTab) { + toggleTab(tab: PanelTab) { if (this.currentTab === tab) { - this.hide(tab); + this.hideTab(tab); } else { - this.show(tab); + this.showTab(tab); } } showDefault() { const openedTab = this.tabs.find((tab) => tab.node.classList.contains('tab-pane') && tab.node.classList.contains('active')); if (openedTab) { - this.hide(openedTab); + this.hideTab(openedTab); } } - show(tab: PanelTab) { + showTab(tab: PanelTab) { if (this.currentTab) { this.currentTab.hide(); } @@ -48,9 +48,17 @@ export default class PanelTabContainer { this.currentTab = tab; } - hide(tab: PanelTab) { + hideTab(tab: PanelTab) { tab.hide(); this.defaultTab.show(); this.currentTab = this.defaultTab; } + + showCurrentTab() { + this.currentTab.show(); + } + + hideCurrentTab() { + this.currentTab.hide(); + } } From 62fe47a553313b39138f3e3add3230f5d3542e6f Mon Sep 17 00:00:00 2001 From: Holger Stitz Date: Fri, 7 Feb 2020 14:09:27 +0100 Subject: [PATCH 18/63] Remove SearchBoxProvider constructor + parameter The parameter are unused in the class and can be removed. --- src/lineup/internal/LineUpPanelActions.ts | 2 +- src/lineup/internal/panel/SearchBoxProvider.ts | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/lineup/internal/LineUpPanelActions.ts b/src/lineup/internal/LineUpPanelActions.ts index a91caadfd..414e53abf 100644 --- a/src/lineup/internal/LineUpPanelActions.ts +++ b/src/lineup/internal/LineUpPanelActions.ts @@ -84,7 +84,7 @@ export default class LineUpPanelActions extends EventHandler { this.header = new PanelHeader(this.node); - this.searchBoxProvider = new SearchBoxProvider(provider, options); + this.searchBoxProvider = new SearchBoxProvider(); if (this.options.enableSidePanel === 'top') { this.node.classList.add('lu-side-panel-top'); diff --git a/src/lineup/internal/panel/SearchBoxProvider.ts b/src/lineup/internal/panel/SearchBoxProvider.ts index 840eeecb0..ced613dd0 100644 --- a/src/lineup/internal/panel/SearchBoxProvider.ts +++ b/src/lineup/internal/panel/SearchBoxProvider.ts @@ -5,10 +5,6 @@ export default class SearchBoxProvider { private searchBoxes: SearchBox[] = []; - constructor(private provider: LocalDataProvider, private options: any) { - - } - get length(): number { return this.searchBoxes.length; } From f6aec93f439beeadfac658207cbdbe43bc8ccf23 Mon Sep 17 00:00:00 2001 From: Holger Stitz Date: Fri, 7 Feb 2020 14:09:37 +0100 Subject: [PATCH 19/63] Add documentation to SearchBoxProvider --- .../internal/panel/SearchBoxProvider.ts | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/lineup/internal/panel/SearchBoxProvider.ts b/src/lineup/internal/panel/SearchBoxProvider.ts index ced613dd0..d29481b55 100644 --- a/src/lineup/internal/panel/SearchBoxProvider.ts +++ b/src/lineup/internal/panel/SearchBoxProvider.ts @@ -1,18 +1,32 @@ -import {SearchBox, LocalDataProvider, IGroupSearchItem} from 'lineupjs'; +import {SearchBox, LocalDataProvider, IGroupSearchItem, ISearchBoxOptions} from 'lineupjs'; import {ISearchOption} from '../LineUpPanelActions'; import i18n from 'phovea_core/src/i18n'; + +/** + * The SearchBoxProvider allows creating multiple LineUp SearchBoxes and stores them internally in a list. + * All created search boxes can be updated simultaneously with a list of searchable items. + */ export default class SearchBoxProvider { + /** + * List of created LineUp SearchBoxes + */ private searchBoxes: SearchBox[] = []; get length(): number { return this.searchBoxes.length; } - createSearchBox(): SearchBox { - const searchBox = new SearchBox({ + /** + * Create a new LineUp SearchBox. The instance is added to the internal list and returned. + * @returns A new LineUp SearchBox instance + */ + createSearchBox(options?: Partial>): SearchBox { + const mergedOptions = Object.assign({ placeholder: i18n.t('tdp:core.lineup.LineupPanelActions.searchPlaceholder') - }); + }, options); + + const searchBox = new SearchBox(mergedOptions); searchBox.on(SearchBox.EVENT_SELECT, (item) => { item.action(); @@ -23,7 +37,12 @@ export default class SearchBoxProvider { return searchBox; } + /** + * Set the passed items to all previously created search box instances. + * @param items List of searchable items for the SearchBox + */ update(items: (ISearchOption | IGroupSearchItem)[]) { this.searchBoxes.forEach((searchBox) => searchBox.data = items); } + } From 9bc87b1d2e9e5f84e39e45823aa27fbc02cfdf9b Mon Sep 17 00:00:00 2001 From: Holger Stitz Date: Fri, 7 Feb 2020 14:20:51 +0100 Subject: [PATCH 20/63] Add documentation to PanelButton --- src/lineup/internal/panel/PanelButton.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/lineup/internal/panel/PanelButton.ts b/src/lineup/internal/panel/PanelButton.ts index 791c182a5..7126284ef 100644 --- a/src/lineup/internal/panel/PanelButton.ts +++ b/src/lineup/internal/panel/PanelButton.ts @@ -1,11 +1,26 @@ - +/** + * Interface for the LineUp panel button + */ export interface IPanelButton { + /** + * DOM node of the LineUp panel button + */ readonly node: HTMLElement; } +/** + * Plain HTML button with a custom title, CSS class and an onClick function + */ export default class PanelButton implements IPanelButton { readonly node: HTMLElement; + /** + * Constructor of the PanelButton + * @param parent The parent HTML DOM element + * @param title String that is used for the title attribute + * @param linkClass CSS classes to apply + * @param onClick Function that should be executed on button click + */ constructor(parent: HTMLElement, title: string, linkClass: string, onClick: () => void) { this.node = parent.ownerDocument.createElement('button'); this.node.className = linkClass; @@ -17,6 +32,7 @@ export default class PanelButton implements IPanelButton { }); } } + export class PanelNavButton implements IPanelButton { readonly node: HTMLElement; From 0105a346a3adb948ee5147b2abb4ef7e90a77c46 Mon Sep 17 00:00:00 2001 From: Holger Stitz Date: Fri, 7 Feb 2020 14:37:42 +0100 Subject: [PATCH 21/63] Add documentation to PanelHeader --- src/lineup/internal/panel/PanelHeader.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/lineup/internal/panel/PanelHeader.ts b/src/lineup/internal/panel/PanelHeader.ts index 6ca56c7e3..6cbf97d49 100644 --- a/src/lineup/internal/panel/PanelHeader.ts +++ b/src/lineup/internal/panel/PanelHeader.ts @@ -1,6 +1,8 @@ import {IPanelButton} from './PanelButton'; - +/** + * The panel header contains a list of panel buttons. + */ export default class PanelHeader { readonly node: HTMLElement; @@ -12,6 +14,10 @@ export default class PanelHeader { parent.appendChild(this.node); } + /** + * Add a panel button to this header + * @param button Panel button instance to add + */ addButton(button: IPanelButton) { this.buttons = [...this.buttons, button]; this.node.appendChild(button.node); From e570f1880e519ff17afc7b1b97a8567b04fa443f Mon Sep 17 00:00:00 2001 From: Holger Stitz Date: Fri, 7 Feb 2020 14:37:56 +0100 Subject: [PATCH 22/63] Rename variable in PanelRankingButton --- src/lineup/internal/panel/PanelRankingButton.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lineup/internal/panel/PanelRankingButton.ts b/src/lineup/internal/panel/PanelRankingButton.ts index 33839c858..f0b05abf1 100644 --- a/src/lineup/internal/panel/PanelRankingButton.ts +++ b/src/lineup/internal/panel/PanelRankingButton.ts @@ -11,9 +11,9 @@ export default class PanelRankingButton implements IPanelButton { this.node.addEventListener('click', (evt) => { evt.stopPropagation(); evt.preventDefault(); - const first = this.provider.getRankings()[0]; - if (first) { - onClick(first); + const firstRanking = this.provider.getRankings()[0]; + if (firstRanking) { + onClick(firstRanking); } }); } From b6397e81ca9e561a3c026c8ca5e765b5b1bb104d Mon Sep 17 00:00:00 2001 From: Holger Stitz Date: Fri, 7 Feb 2020 15:39:13 +0100 Subject: [PATCH 23/63] Revert some i18n strings in LineUpPanelActions --- src/lineup/internal/LineUpPanelActions.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lineup/internal/LineUpPanelActions.ts b/src/lineup/internal/LineUpPanelActions.ts index 414e53abf..392600b95 100644 --- a/src/lineup/internal/LineUpPanelActions.ts +++ b/src/lineup/internal/LineUpPanelActions.ts @@ -350,12 +350,12 @@ export default class LineUpPanelActions extends EventHandler { }; if (this.options.enableAddingCombiningColumns) { - const combiningColumns = this.groupedDialog('Combining Columns', [ - {text: 'Weighted Sum', id: 'weightedSum', action: () => this.addColumn(createStackDesc('Weighted Sum'))}, - {text: 'Scripted Combination', id: 'scriptedCombination', action: () => this.addColumn(createScriptDesc('Scripted Combination'))}, - {text: 'Nested', id: 'nested', action: () => this.addColumn(createNestedDesc('Nested'))}, - {text: 'Min/Max/Mean Combination', id: 'reduce', action: () => this.addColumn(createReduceDesc())}, - {text: 'Imposition', id: 'imposition', action: () => this.addColumn(createImpositionDesc())} + const combiningColumns = this.groupedDialog(i18n.t('tdp:core.lineup.LineupPanelActions.combiningColumns'), [ + {text: i18n.t('tdp:core.lineup.LineupPanelActions.weightedSum'), id: 'weightedSum', action: () => this.addColumn(createStackDesc(i18n.t('tdp:core.lineup.LineupPanelActions.weightedSum')))}, + {text: i18n.t('tdp:core.lineup.LineupPanelActions.scriptedCombination'), id: 'scriptedCombination', action: () => this.addColumn(createScriptDesc(i18n.t('tdp:core.lineup.LineupPanelActions.scriptedCombination')))}, + {text: i18n.t('tdp:core.lineup.LineupPanelActions.nested'), id: 'nested', action: () => this.addColumn(createNestedDesc(i18n.t('tdp:core.lineup.LineupPanelActions.nested')))}, + {text: i18n.t('tdp:core.lineup.LineupPanelActions.reduce'), id: 'reduce', action: () => this.addColumn(createReduceDesc())}, + {text: i18n.t('tdp:core.lineup.LineupPanelActions.imposition'), id: 'imposition', action: () => this.addColumn(createImpositionDesc())} ]); specialColumnsOption.children.push(combiningColumns); } From fec77c5a664676ca49284f941685cff3e6e2de16 Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Mon, 17 Feb 2020 14:25:04 +0100 Subject: [PATCH 24/63] * Improve typings and docu for epTdpCoreLineupPanelTab datavisyn/tdp_core#285 --- src/extensions.ts | 31 +++++++++++++++++++++-- src/lineup/internal/LineUpPanelActions.ts | 10 ++++---- src/lineup/internal/panel/PanelTab.ts | 8 +++--- 3 files changed, 38 insertions(+), 11 deletions(-) diff --git a/src/extensions.ts b/src/extensions.ts index bfbba17f3..03edfed58 100644 --- a/src/extensions.ts +++ b/src/extensions.ts @@ -6,9 +6,10 @@ import {IEventHandler} from 'phovea_core/src/event'; import {IScore, IAdditionalColumnDesc} from './lineup'; import {RangeLike} from 'phovea_core/src/range'; import {IDType} from 'phovea_core/src/idtype'; -import {IColumnDesc, Column} from 'lineupjs'; +import {IColumnDesc, Column, LocalDataProvider} from 'lineupjs'; import {EViewMode} from './views/interfaces'; import {AppHeader} from 'phovea_ui/src/header'; +import {IPanelTabDesc} from './lineup/internal/panel/PanelTab'; export * from './tour/extensions'; @@ -16,7 +17,6 @@ export const EXTENSION_POINT_TDP_SCORE = 'tdpScore'; export const EXTENSION_POINT_TDP_SCORE_IMPL = 'tdpScoreImpl'; export const EXTENSION_POINT_TDP_SCORE_LOADER = 'tdpScoreLoader'; export const EXTENSION_POINT_TDP_RANKING_BUTTON = 'tdpRankingButton'; -export const EXTENSION_POINT_TDP_LINEUP_PANEL_TAB = 'tdpLineupPanelTab'; export const EXTENSION_POINT_TDP_VIEW = 'tdpView'; export const EXTENSION_POINT_TDP_INSTANT_VIEW = 'tdpInstantView'; export const EXTENSION_POINT_TDP_APP_EXTENSION = 'tdpAppExtension'; @@ -24,6 +24,16 @@ export const EXTENSION_POINT_TDP_APP_EXTENSION = 'tdpAppExtension'; export const EXTENSION_POINT_TDP_LIST_FILTERS = 'tdpListFilters'; export const EXTENSION_POINT_TDP_VIEW_GROUPS = 'tdpViewGroups'; +/** + * Register a new tab to the LineupSidePanel. + * Consists of a button/header to open the tab content and the tab content itself + * @factoryParam {HTMLElement} parent The node of the tab content created through the extension point + * @factoryParam {LocalDataProvider} provider The data of the current ranking + * @factoryParam {IPanelTabExtensionDesc} desc The phovea extension point description + * @factoryParam {PanelTabEvents} events Listen when the tab closes or opens + */ +export const EP_TDP_CORE_LINEUP_PANEL_TAB = 'epTdpCoreLineupPanelTab'; + /** * Register new form elements for the form builder. Form elements must implement the `IFormElement`. * @@ -137,6 +147,23 @@ export interface IScoreColumnPatcherExtensionDesc extends IPluginDesc { load(): Promise; } +export interface IPanelTabExtension { + desc: IPanelTabExtensionDesc; + factory(parent: HTMLElement, provider: LocalDataProvider, desc: IRankingButtonExtensionDesc, idType: IDType, extraArgs: object): Promise; +} + +export interface IPanelTabExtensionDesc extends IPluginDesc { + /** + * @param headerCssClass css class for the button/header of the LineupSidePanel tab + * @param headerTitle title of the above button/header + * @param tabDesc description of the tab e.g { width:'40' } + */ + headerCssClass: string; + headerTitle: string; + tabDesc: IPanelTabDesc; + + load(): Promise; +} export interface IRankingButtonExtension { desc: IRankingButtonExtensionDesc; factory(desc: IRankingButtonExtensionDesc, idType: IDType, extraArgs: object): Promise; diff --git a/src/lineup/internal/LineUpPanelActions.ts b/src/lineup/internal/LineUpPanelActions.ts index 392600b95..b5522e967 100644 --- a/src/lineup/internal/LineUpPanelActions.ts +++ b/src/lineup/internal/LineUpPanelActions.ts @@ -5,7 +5,7 @@ import {IPlugin, IPluginDesc, list as listPlugins} from 'phovea_core/src/plugin' import {editDialog} from '../../storage'; import { IScoreLoader, EXTENSION_POINT_TDP_SCORE_LOADER, EXTENSION_POINT_TDP_SCORE, EXTENSION_POINT_TDP_RANKING_BUTTON, - IScoreLoaderExtensionDesc, IRankingButtonExtension, IRankingButtonExtensionDesc, EXTENSION_POINT_TDP_LINEUP_PANEL_TAB + IScoreLoaderExtensionDesc, IRankingButtonExtension, IRankingButtonExtensionDesc, EP_TDP_CORE_LINEUP_PANEL_TAB, IPanelTabExtensionDesc } from '../../extensions'; import {EventHandler} from 'phovea_core/src/event'; import {IARankingViewOptions} from '../ARankingView'; @@ -13,7 +13,7 @@ import {lazyDialogModule} from '../../dialogs'; import PanelButton, {IPanelButton, PanelNavButton} from './panel/PanelButton'; import PanelTabContainer from './panel/PanelTabContainer'; import PanelDownloadButton from './panel/PanelDownloadButton'; -import {PanelTab, SidePanelTab} from './panel/PanelTab'; +import {PanelTab, SidePanelTab, IPanelTabDesc} from './panel/PanelTab'; import SearchBoxProvider from './panel/SearchBoxProvider'; import PanelHeader from './panel/PanelHeader'; import PanelRankingButton from './panel/PanelRankingButton'; @@ -234,9 +234,9 @@ export default class LineUpPanelActions extends EventHandler { } private appendExtraTabs(buttons: HTMLElement) { - const plugins = listPlugins(EXTENSION_POINT_TDP_LINEUP_PANEL_TAB); + const plugins = listPlugins(EP_TDP_CORE_LINEUP_PANEL_TAB); return plugins.map((plugin) => { - const tab = new PanelTab(this.tabContainer.node, plugin.options); + const tab = new PanelTab(this.tabContainer.node, plugin.tabDesc); this.tabContainer.addTab(tab); let isLoaded = false; @@ -261,7 +261,7 @@ export default class LineUpPanelActions extends EventHandler { }); } }; - return new PanelNavButton(buttons, tab.node, plugin.title, 'fa ' + plugin.cssClass, listener); + return new PanelNavButton(buttons, tab.node, plugin.headerTitle, 'fa ' + plugin.headerCssClass, listener); }); } diff --git a/src/lineup/internal/panel/PanelTab.ts b/src/lineup/internal/panel/PanelTab.ts index 1522b26bf..854646670 100644 --- a/src/lineup/internal/panel/PanelTab.ts +++ b/src/lineup/internal/panel/PanelTab.ts @@ -2,8 +2,8 @@ import {SidePanel, SearchBox} from 'lineupjs'; import {ISearchOption} from '../LineUpPanelActions'; import {EventHandler} from 'phovea_core/src/event'; -export interface IPanelTabOptions { - tabWidth: string; +export interface IPanelTabDesc { + width: string; } export class PanelTabEvents extends EventHandler { @@ -19,14 +19,14 @@ export class PanelTab { readonly node: HTMLElement; readonly events: PanelTabEvents; - constructor(parent: HTMLElement, options?: Partial) { + constructor(parent: HTMLElement, options?: IPanelTabDesc) { this.events = new PanelTabEvents(); this.node = parent.ownerDocument.createElement('div'); this.node.classList.add('tab-pane'); const o = Object.assign({}, options); - this.node.style.width = o.tabWidth || null; + this.node.style.width = o.width + 'em' || null; } show() { From de333092c0b82a299913634013a071d640b981cf Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Mon, 17 Feb 2020 15:44:03 +0100 Subject: [PATCH 25/63] Refactor PanelNavButton & extend docu datavisyn/tdp_core#285 --- src/lineup/internal/LineUpPanelActions.ts | 2 +- src/lineup/internal/panel/PanelButton.ts | 26 +++++++++++++---------- src/lineup/internal/panel/PanelTab.ts | 4 ++++ 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/lineup/internal/LineUpPanelActions.ts b/src/lineup/internal/LineUpPanelActions.ts index b5522e967..9e9dc2907 100644 --- a/src/lineup/internal/LineUpPanelActions.ts +++ b/src/lineup/internal/LineUpPanelActions.ts @@ -261,7 +261,7 @@ export default class LineUpPanelActions extends EventHandler { }); } }; - return new PanelNavButton(buttons, tab.node, plugin.headerTitle, 'fa ' + plugin.headerCssClass, listener); + return new PanelNavButton(buttons, tab, plugin.headerTitle, 'fa ' + plugin.headerCssClass, listener); }); } diff --git a/src/lineup/internal/panel/PanelButton.ts b/src/lineup/internal/panel/PanelButton.ts index 7126284ef..1f1410dc0 100644 --- a/src/lineup/internal/panel/PanelButton.ts +++ b/src/lineup/internal/panel/PanelButton.ts @@ -1,3 +1,4 @@ +import {PanelTab} from './PanelTab'; /** * Interface for the LineUp panel button */ @@ -33,27 +34,30 @@ export default class PanelButton implements IPanelButton { } } +/** + * HTML button with a custom title, CSS class, an onClick function + * Acts as tab header/button and highlights itself when clicked depending on if the tab body is open or closed + */ export class PanelNavButton implements IPanelButton { readonly node: HTMLElement; - constructor(parent: HTMLElement, private readonly tabNode: HTMLElement, title: string, linkClass: string, onClick: () => void) { + /** + * Constructor of the PanelButton + * @param parent The parent HTML DOM element + * @param tab The tab it is connected to + * @param title String that is used for the title attribute + * @param linkClass CSS classes to apply + * @param onClick Function that should be executed on button click + */ + constructor(parent: HTMLElement, private readonly tab: PanelTab, title: string, linkClass: string, onClick: () => void) { this.node = parent.ownerDocument.createElement('button'); this.node.className = linkClass; this.node.title = title; this.node.addEventListener('click', (evt) => { evt.stopPropagation(); evt.preventDefault(); - this.highlight(); + this.node.classList.toggle('active', this.tab.isClosed()) onClick(); }); } - private highlight() { - if (!this.tabNode.classList.contains('active')) { - this.node.classList.add('active'); - - } else { - this.node.classList.remove('active'); - } - - } } diff --git a/src/lineup/internal/panel/PanelTab.ts b/src/lineup/internal/panel/PanelTab.ts index 854646670..c6b42209d 100644 --- a/src/lineup/internal/panel/PanelTab.ts +++ b/src/lineup/internal/panel/PanelTab.ts @@ -38,6 +38,10 @@ export class PanelTab { this.node.classList.remove('active'); this.events.fire(PanelTabEvents.HIDE_PANEL); } + + isClosed() { + return !this.node.classList.contains('active') + } } export class SidePanelTab extends PanelTab { From 56e4b703c2c3da96dd151e1a58136fdf1239fc1e Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Mon, 17 Feb 2020 16:50:23 +0100 Subject: [PATCH 26/63] extend lineupSidepanel documentation datavisyn/tdp_core#285 --- src/lineup/internal/LineUpPanelActions.ts | 6 +++--- ...nloadButton.ts => PanelDownloadButtonContainer.ts} | 11 +++++++++-- src/lineup/internal/panel/PanelTab.ts | 6 +++--- src/styles/_view_lineup.scss | 2 -- 4 files changed, 15 insertions(+), 10 deletions(-) rename src/lineup/internal/panel/{PanelDownloadButton.ts => PanelDownloadButtonContainer.ts} (90%) diff --git a/src/lineup/internal/LineUpPanelActions.ts b/src/lineup/internal/LineUpPanelActions.ts index 9e9dc2907..2216ea193 100644 --- a/src/lineup/internal/LineUpPanelActions.ts +++ b/src/lineup/internal/LineUpPanelActions.ts @@ -12,13 +12,13 @@ import {IARankingViewOptions} from '../ARankingView'; import {lazyDialogModule} from '../../dialogs'; import PanelButton, {IPanelButton, PanelNavButton} from './panel/PanelButton'; import PanelTabContainer from './panel/PanelTabContainer'; -import PanelDownloadButton from './panel/PanelDownloadButton'; import {PanelTab, SidePanelTab, IPanelTabDesc} from './panel/PanelTab'; import SearchBoxProvider from './panel/SearchBoxProvider'; import PanelHeader from './panel/PanelHeader'; import PanelRankingButton from './panel/PanelRankingButton'; import PanelAddColumnButton from './panel/PanelAddColumnButton'; import i18n from 'phovea_core/src/i18n'; +import PanelDownloadButtonContainer from './panel/PanelDownloadButtonContainer' export interface ISearchOption { text: string; @@ -183,8 +183,8 @@ export default class LineUpPanelActions extends EventHandler { } if (this.options.enableDownload) { - const downloadButton = new PanelDownloadButton(buttons, this.provider, this.isTopMode); - this.header.addButton(downloadButton); + const downloadButtonContainer = new PanelDownloadButtonContainer(buttons, this.provider, this.isTopMode); + this.header.addButton(downloadButtonContainer); } if (this.options.enableZoom) { diff --git a/src/lineup/internal/panel/PanelDownloadButton.ts b/src/lineup/internal/panel/PanelDownloadButtonContainer.ts similarity index 90% rename from src/lineup/internal/panel/PanelDownloadButton.ts rename to src/lineup/internal/panel/PanelDownloadButtonContainer.ts index 348634350..e7b2b1a35 100644 --- a/src/lineup/internal/panel/PanelDownloadButton.ts +++ b/src/lineup/internal/panel/PanelDownloadButtonContainer.ts @@ -2,10 +2,17 @@ import {LocalDataProvider} from 'lineupjs'; import {exportLogic} from '../export'; import {IPanelButton} from './PanelButton'; import i18n from 'phovea_core/src/i18n'; -export default class PanelDownloadButton implements IPanelButton { + + +/** + * An HTML div element tha contains 2 children: + * A button that toggles a dropdown to download select/all rows of the ranking + * A dropwdown HTMLUListElement + */ +export default class PanelDownloadButtonContainer implements IPanelButton { readonly node: HTMLElement; - constructor(parent: HTMLElement, private provider: LocalDataProvider, isTopMode: boolean) { + constructor(parent: HTMLElement, private provider: LocalDataProvider, isTopMode:boolean) { this.node = parent.ownerDocument.createElement('div'); this.node.classList.add('btn-group', 'download-data-dropdown'); this.node.innerHTML = ` diff --git a/src/lineup/internal/panel/PanelTab.ts b/src/lineup/internal/panel/PanelTab.ts index c6b42209d..03c4a6372 100644 --- a/src/lineup/internal/panel/PanelTab.ts +++ b/src/lineup/internal/panel/PanelTab.ts @@ -29,17 +29,17 @@ export class PanelTab { this.node.style.width = o.width + 'em' || null; } - show() { + public show() { this.node.classList.add('active'); this.events.fire(PanelTabEvents.SHOW_PANEL); } - hide() { + public hide() { this.node.classList.remove('active'); this.events.fire(PanelTabEvents.HIDE_PANEL); } - isClosed() { + public isClosed() { return !this.node.classList.contains('active') } } diff --git a/src/styles/_view_lineup.scss b/src/styles/_view_lineup.scss index 979c1ddd8..b9d2251e9 100644 --- a/src/styles/_view_lineup.scss +++ b/src/styles/_view_lineup.scss @@ -113,8 +113,6 @@ $lu_assets: '~lineupjs/src/assets'; height: 19px; } - ////////////////////// - ///////////////////// .lu-side-panel-wrapper { display: flex; flex-direction: column; From 0ddb1eb50e9aa7c77344d88c67da980f0893bfe4 Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Tue, 18 Feb 2020 08:23:21 +0100 Subject: [PATCH 27/63] fix linter error datavisyn/tdp_core#285 --- src/lineup/internal/LineUpPanelActions.ts | 2 +- src/lineup/internal/panel/PanelButton.ts | 4 ++-- src/lineup/internal/panel/PanelTab.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lineup/internal/LineUpPanelActions.ts b/src/lineup/internal/LineUpPanelActions.ts index 2216ea193..e45e858ef 100644 --- a/src/lineup/internal/LineUpPanelActions.ts +++ b/src/lineup/internal/LineUpPanelActions.ts @@ -18,7 +18,7 @@ import PanelHeader from './panel/PanelHeader'; import PanelRankingButton from './panel/PanelRankingButton'; import PanelAddColumnButton from './panel/PanelAddColumnButton'; import i18n from 'phovea_core/src/i18n'; -import PanelDownloadButtonContainer from './panel/PanelDownloadButtonContainer' +import PanelDownloadButtonContainer from './panel/PanelDownloadButtonContainer'; export interface ISearchOption { text: string; diff --git a/src/lineup/internal/panel/PanelButton.ts b/src/lineup/internal/panel/PanelButton.ts index 1f1410dc0..97565e42b 100644 --- a/src/lineup/internal/panel/PanelButton.ts +++ b/src/lineup/internal/panel/PanelButton.ts @@ -41,7 +41,7 @@ export default class PanelButton implements IPanelButton { export class PanelNavButton implements IPanelButton { readonly node: HTMLElement; - /** +/** * Constructor of the PanelButton * @param parent The parent HTML DOM element * @param tab The tab it is connected to @@ -56,7 +56,7 @@ export class PanelNavButton implements IPanelButton { this.node.addEventListener('click', (evt) => { evt.stopPropagation(); evt.preventDefault(); - this.node.classList.toggle('active', this.tab.isClosed()) + this.node.classList.toggle('active', this.tab.isClosed()); onClick(); }); } diff --git a/src/lineup/internal/panel/PanelTab.ts b/src/lineup/internal/panel/PanelTab.ts index 03c4a6372..603633654 100644 --- a/src/lineup/internal/panel/PanelTab.ts +++ b/src/lineup/internal/panel/PanelTab.ts @@ -40,7 +40,7 @@ export class PanelTab { } public isClosed() { - return !this.node.classList.contains('active') + return !this.node.classList.contains('active'); } } From 1b8ded2ea5b1137b79f928cfbe83f08f9e0d8392 Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Wed, 19 Feb 2020 10:56:04 +0100 Subject: [PATCH 28/63] refactor PanelTab.ts datavisyn/tdp_core#257 --- src/lineup/internal/LineUpPanelActions.ts | 33 +++--- src/lineup/internal/panel/PanelButton.ts | 27 +++-- .../panel/PanelDownloadButtonContainer.ts | 2 +- src/lineup/internal/panel/PanelHeader.ts | 26 +++-- src/styles/_view_lineup.scss | 100 +++++++++++------- 5 files changed, 112 insertions(+), 76 deletions(-) diff --git a/src/lineup/internal/LineUpPanelActions.ts b/src/lineup/internal/LineUpPanelActions.ts index e45e858ef..c24ce4e96 100644 --- a/src/lineup/internal/LineUpPanelActions.ts +++ b/src/lineup/internal/LineUpPanelActions.ts @@ -92,7 +92,7 @@ export default class LineUpPanelActions extends EventHandler { } else { const sidePanel = new SidePanelTab(this.node, this.searchBoxProvider.createSearchBox(), ctx, doc); this.panel = sidePanel.panel; - + this.appendLineUpNav(this.header, sidePanel); this.tabContainer = new PanelTabContainer(this.node); this.tabContainer.addTab(sidePanel); this.tabContainer.showTab(sidePanel); @@ -102,11 +102,6 @@ export default class LineUpPanelActions extends EventHandler { this.collapse = options.enableSidePanel === 'top' || options.enableSidePanel === 'collapsed'; } - removeTabButtonHighlighting() { - this.tabContainer.showDefault(); - this.header.removeHighlighting(); - } - forceCollapse() { this.wasCollapsed = this.collapse; this.collapse = true; @@ -129,7 +124,9 @@ export default class LineUpPanelActions extends EventHandler { set collapse(value: boolean) { this.node.classList.toggle('collapsed', value); if (value && this.options.enableSidePanel !== 'top') { - this.removeTabButtonHighlighting(); + this.tabContainer.hideCurrentTab(); + } else if (this.options.enableSidePanel !== 'top') { + this.tabContainer.showCurrentTab(); } } @@ -153,7 +150,8 @@ export default class LineUpPanelActions extends EventHandler { } private init() { - const buttons = this.header.node; + const buttons = this.header.buttonGroupNode; + const navs = this.header.navGroupNode; if (!this.isTopMode && this.options.enableSidePanelCollapsing) { // top mode doesn't need collapse feature const listener = () => { @@ -207,12 +205,21 @@ export default class LineUpPanelActions extends EventHandler { } if (!this.isTopMode) { - this.appendExtraTabs(buttons).forEach((button: PanelButton) => { - this.header.addButton(button); + this.appendExtraTabs(navs).forEach((nav: PanelNavButton) => { + this.header.addNav(nav); }); } } + appendLineUpNav(header, sidePanelTab: PanelTab) { + const listener = () => { + this.tabContainer.showTab(sidePanelTab); + }; + const lineupNavButton = new PanelNavButton(header.node, sidePanelTab, 'Lineup Config', 'fa fa-adjust lineup-nav', listener, true); + + header.navGroupNode.appendChild(lineupNavButton.node); + } + setViolation(violation?: string) { if (violation) { this.overview.dataset.violation = violation; @@ -233,7 +240,7 @@ export default class LineUpPanelActions extends EventHandler { }); } - private appendExtraTabs(buttons: HTMLElement) { + private appendExtraTabs(navs: HTMLElement) { const plugins = listPlugins(EP_TDP_CORE_LINEUP_PANEL_TAB); return plugins.map((plugin) => { const tab = new PanelTab(this.tabContainer.node, plugin.tabDesc); @@ -248,7 +255,7 @@ export default class LineUpPanelActions extends EventHandler { this.tabContainer.showTab(tab); } else { - this.tabContainer.toggleTab(tab); + this.tabContainer.showTab(tab); } } else { @@ -261,7 +268,7 @@ export default class LineUpPanelActions extends EventHandler { }); } }; - return new PanelNavButton(buttons, tab, plugin.headerTitle, 'fa ' + plugin.headerCssClass, listener); + return new PanelNavButton(navs, tab, plugin.headerTitle, 'fa ' + plugin.headerCssClass, listener); }); } diff --git a/src/lineup/internal/panel/PanelButton.ts b/src/lineup/internal/panel/PanelButton.ts index 97565e42b..06c59f540 100644 --- a/src/lineup/internal/panel/PanelButton.ts +++ b/src/lineup/internal/panel/PanelButton.ts @@ -41,22 +41,21 @@ export default class PanelButton implements IPanelButton { export class PanelNavButton implements IPanelButton { readonly node: HTMLElement; -/** - * Constructor of the PanelButton - * @param parent The parent HTML DOM element - * @param tab The tab it is connected to - * @param title String that is used for the title attribute - * @param linkClass CSS classes to apply - * @param onClick Function that should be executed on button click - */ - constructor(parent: HTMLElement, private readonly tab: PanelTab, title: string, linkClass: string, onClick: () => void) { - this.node = parent.ownerDocument.createElement('button'); - this.node.className = linkClass; - this.node.title = title; + /** + * Constructor of the PanelButton + * @param parent The parent HTML DOM element + * @param tab The tab it is connected to + * @param title String that is used for the title attribute + * @param linkClass CSS classes to apply + * @param onClick Function that should be executed on button click + */ + constructor(parent: HTMLElement, private readonly tab: PanelTab, title: string, linkClass: string, onClick: () => void, defaultActive?) { + + this.node = parent.ownerDocument.createElement('li'); + this.node.className = defaultActive ? 'active' : ' '; + this.node.insertAdjacentHTML('afterbegin', ``); this.node.addEventListener('click', (evt) => { - evt.stopPropagation(); evt.preventDefault(); - this.node.classList.toggle('active', this.tab.isClosed()); onClick(); }); } diff --git a/src/lineup/internal/panel/PanelDownloadButtonContainer.ts b/src/lineup/internal/panel/PanelDownloadButtonContainer.ts index e7b2b1a35..812d0d29c 100644 --- a/src/lineup/internal/panel/PanelDownloadButtonContainer.ts +++ b/src/lineup/internal/panel/PanelDownloadButtonContainer.ts @@ -7,7 +7,7 @@ import i18n from 'phovea_core/src/i18n'; /** * An HTML div element tha contains 2 children: * A button that toggles a dropdown to download select/all rows of the ranking - * A dropwdown HTMLUListElement + * A dropwdown HTMLUListElement with download links */ export default class PanelDownloadButtonContainer implements IPanelButton { readonly node: HTMLElement; diff --git a/src/lineup/internal/panel/PanelHeader.ts b/src/lineup/internal/panel/PanelHeader.ts index 6cbf97d49..f99903b09 100644 --- a/src/lineup/internal/panel/PanelHeader.ts +++ b/src/lineup/internal/panel/PanelHeader.ts @@ -1,4 +1,4 @@ -import {IPanelButton} from './PanelButton'; +import {IPanelButton, PanelNavButton} from './PanelButton'; /** * The panel header contains a list of panel buttons. @@ -6,12 +6,20 @@ import {IPanelButton} from './PanelButton'; export default class PanelHeader { readonly node: HTMLElement; - + readonly buttonGroupNode: HTMLElement; + readonly navGroupNode: HTMLElement; private buttons: IPanelButton[] = []; + private navTabs: PanelNavButton[] = []; constructor(parent: HTMLElement) { this.node = parent.ownerDocument.createElement('header'); parent.appendChild(this.node); + this.buttonGroupNode = this.node.ownerDocument.createElement('div'); + this.navGroupNode = this.node.ownerDocument.createElement('ul'); + this.buttonGroupNode.classList.add('button-group'); + this.navGroupNode.className = 'nav nav-tabs'; + this.node.appendChild(this.buttonGroupNode); + this.node.appendChild(this.navGroupNode); } /** @@ -20,13 +28,15 @@ export default class PanelHeader { */ addButton(button: IPanelButton) { this.buttons = [...this.buttons, button]; - this.node.appendChild(button.node); + this.buttonGroupNode.appendChild(button.node); } - removeHighlighting() { - const highlightedButton = this.buttons.find((button) => button.node.classList.contains('active')); - if (highlightedButton) { - highlightedButton.node.classList.remove('active'); - } + /** + * Add a PanelNavTab to this header + * @param button Panel button instance to add + */ + addNav(nav: PanelNavButton) { + this.navTabs = [...this.navTabs, nav]; + this.navGroupNode.appendChild(nav.node); } } diff --git a/src/styles/_view_lineup.scss b/src/styles/_view_lineup.scss index b9d2251e9..081705603 100644 --- a/src/styles/_view_lineup.scss +++ b/src/styles/_view_lineup.scss @@ -1,6 +1,6 @@ -@import './vars'; -@import '~font-awesome/scss/variables'; -@import '~font-awesome/scss/mixins'; +@import "./vars"; +@import "~font-awesome/scss/variables"; +@import "~font-awesome/scss/mixins"; &.lineup { display: flex; @@ -11,21 +11,21 @@ line-height: 1; } - >div { + > div { flex: 1 1 auto; position: relative; } &.overview-detail { - >div { + > div { display: flex; flex-direction: column; - >header { + > header { flex: 0 0 auto; } - >div { + > div { flex: 1 1 auto; position: relative; } @@ -47,7 +47,7 @@ margin: 0; } - &.lu-filter-table>div:first-of-type>* { + &.lu-filter-table > div:first-of-type > * { flex: 0 0 auto; } } @@ -64,7 +64,7 @@ left: 0; overflow: auto; - >header { + > header { padding-bottom: 1px; // no idea otherwise scrollbar } @@ -73,10 +73,9 @@ } } -$lu_assets: '~lineupjs/src/assets'; +$lu_assets: "~lineupjs/src/assets"; @at-root { - .tdp-ranking-export-form { user-select: none; -moz-user-select: none; @@ -87,14 +86,14 @@ $lu_assets: '~lineupjs/src/assets'; } .tdp-ranking-export-form-handle { - >span { + > span { visibility: hidden; padding: 0 0.5em 0 1em; cursor: grab; } - &:hover>span, - &.dragging>span { + &:hover > span, + &.dragging > span { visibility: visible; } @@ -105,8 +104,8 @@ $lu_assets: '~lineupjs/src/assets'; } } - .lu-side-panel>main>section::before { - content: 'Column Summaries'; + .lu-side-panel > main > section::before { + content: "Column Summaries"; } .lu-hierarchy .lu-search::before { @@ -120,6 +119,7 @@ $lu_assets: '~lineupjs/src/assets'; %lu-panel-button { background: #fff; border: 1px solid #ddd; + border-radius: 0px; padding: 5px 10px; cursor: pointer; z-index: 1; @@ -130,7 +130,7 @@ $lu_assets: '~lineupjs/src/assets'; } } - .lu-side-panel>main>section>div { + .lu-side-panel > main > section > div { flex: 1; } @@ -138,10 +138,24 @@ $lu_assets: '~lineupjs/src/assets'; border-left: 0; } - >header { + > header { margin-bottom: 1em; - display: grid; - grid-template-columns: repeat(auto-fill, minmax(2.8em, 1fr)); + // display: grid; + // grid-template-columns: repeat(auto-fill, minmax(2.8em, 1fr)); + .button-group { + display: flex; + margin-bottom: 0.5em; + } + + ul.nav-tabs { + display: flex; + + & > li > a { + color: black; + padding: 5px 8.5px; + cursor: pointer; + } + } .lu-adder { margin-top: 1em; @@ -153,10 +167,6 @@ $lu_assets: '~lineupjs/src/assets'; @extend %lu-panel-button; height: 2em; } - - button.active { - color: #ffa500 - } } label, @@ -194,7 +204,7 @@ $lu_assets: '~lineupjs/src/assets'; button[data-violation] { position: relative; - color: #FFD700; + color: #ffd700; &::before { content: $fa-var-exclamation-triangle; @@ -204,7 +214,7 @@ $lu_assets: '~lineupjs/src/assets'; content: attr(data-violation); color: black; text-align: left; - background: lighten(#FFD700, 45%); + background: lighten(#ffd700, 45%); hyphens: manual; padding: 5px; width: 12em; @@ -226,17 +236,16 @@ $lu_assets: '~lineupjs/src/assets'; } .download-data-dropdown { - // show the number of selected rows after the element li[data-num-selected-rows]::after { - content: ' ('attr(data-num-selected-rows) ')'; + content: " (" attr(data-num-selected-rows) ")"; display: inline; } // hide the dropdown-header and the next list point (= download link), if no rows are selected // note that this only works for a *single* link as after the header list item! - li[data-num-selected-rows='0'], - li[data-num-selected-rows='0']+li { + li[data-num-selected-rows="0"], + li[data-num-selected-rows="0"] + li { display: none; } } @@ -257,18 +266,30 @@ $lu_assets: '~lineupjs/src/assets'; } } - header { - width: auto; - flex-direction: column; + > header { margin-right: 0; min-width: 0; align-self: flex-start; - - button.active { - color: unset; + width: auto; + flex-direction: column; + .button-group { + display: flex; + flex-direction: column; + } + .nav-tabs { + margin-top: 1em; + + li { + > a { + margin-right: 0px; + @extend %lu-panel-button; + } + } + .lineup-nav { + display: none; //hide lineup tab when sidePanel is collapsed + } } } - .tab-content { display: none; } @@ -289,7 +310,7 @@ $lu_assets: '~lineupjs/src/assets'; } &.once::before { - content: ''; + content: ""; top: -30px; left: -20em; right: 0; @@ -316,7 +337,6 @@ $lu_assets: '~lineupjs/src/assets'; } } - .lu-side-panel-top-form { display: flex; @@ -354,7 +374,7 @@ $lu_assets: '~lineupjs/src/assets'; } &.once::before { - content: ''; + content: ""; top: -10px; left: -2.5em; right: -15em; From 08f0c7f54a829758c4b20b2dd85abfe90cb25b8d Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Wed, 19 Feb 2020 11:35:05 +0100 Subject: [PATCH 29/63] adapt css for side panel top mode datavisyn/tdp_core#257 --- src/lineup/internal/LineUpPanelActions.ts | 2 +- src/lineup/internal/panel/PanelHeader.ts | 11 +++++++---- src/styles/_view_lineup.scss | 9 +++++---- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/lineup/internal/LineUpPanelActions.ts b/src/lineup/internal/LineUpPanelActions.ts index c24ce4e96..a68b02443 100644 --- a/src/lineup/internal/LineUpPanelActions.ts +++ b/src/lineup/internal/LineUpPanelActions.ts @@ -82,7 +82,7 @@ export default class LineUpPanelActions extends EventHandler { this.node = doc.createElement('aside'); this.node.classList.add('lu-side-panel-wrapper'); - this.header = new PanelHeader(this.node); + this.header = new PanelHeader(this.node, this.options.enableSidePanel === 'top'); this.searchBoxProvider = new SearchBoxProvider(); diff --git a/src/lineup/internal/panel/PanelHeader.ts b/src/lineup/internal/panel/PanelHeader.ts index f99903b09..ac80df1c6 100644 --- a/src/lineup/internal/panel/PanelHeader.ts +++ b/src/lineup/internal/panel/PanelHeader.ts @@ -11,15 +11,18 @@ export default class PanelHeader { private buttons: IPanelButton[] = []; private navTabs: PanelNavButton[] = []; - constructor(parent: HTMLElement) { + constructor(parent: HTMLElement, isTopMode: boolean) { this.node = parent.ownerDocument.createElement('header'); parent.appendChild(this.node); this.buttonGroupNode = this.node.ownerDocument.createElement('div'); - this.navGroupNode = this.node.ownerDocument.createElement('ul'); this.buttonGroupNode.classList.add('button-group'); - this.navGroupNode.className = 'nav nav-tabs'; + if (!isTopMode) { + this.navGroupNode = this.node.ownerDocument.createElement('ul'); + this.navGroupNode.className = 'nav nav-tabs'; + this.node.appendChild(this.navGroupNode); + } + this.node.appendChild(this.buttonGroupNode); - this.node.appendChild(this.navGroupNode); } /** diff --git a/src/styles/_view_lineup.scss b/src/styles/_view_lineup.scss index 081705603..a8924fb4a 100644 --- a/src/styles/_view_lineup.scss +++ b/src/styles/_view_lineup.scss @@ -139,9 +139,10 @@ $lu_assets: "~lineupjs/src/assets"; } > header { - margin-bottom: 1em; - // display: grid; - // grid-template-columns: repeat(auto-fill, minmax(2.8em, 1fr)); + & > :not(.collapsed) { + margin-bottom: 1em; + } + .button-group { display: flex; margin-bottom: 0.5em; @@ -354,7 +355,7 @@ $lu_assets: "~lineupjs/src/assets"; float: right; border-left: none; - header { + > header > .button-group { display: flex; flex-direction: row; max-width: unset; From 52c1b1679f555e934d0aeacdad7ce8faf9e23039 Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Wed, 19 Feb 2020 11:52:18 +0100 Subject: [PATCH 30/63] minor change datavisyn/tdp_core#285 --- src/lineup/internal/panel/PanelHeader.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lineup/internal/panel/PanelHeader.ts b/src/lineup/internal/panel/PanelHeader.ts index ac80df1c6..71f0f3149 100644 --- a/src/lineup/internal/panel/PanelHeader.ts +++ b/src/lineup/internal/panel/PanelHeader.ts @@ -16,13 +16,12 @@ export default class PanelHeader { parent.appendChild(this.node); this.buttonGroupNode = this.node.ownerDocument.createElement('div'); this.buttonGroupNode.classList.add('button-group'); + this.node.appendChild(this.buttonGroupNode); if (!isTopMode) { this.navGroupNode = this.node.ownerDocument.createElement('ul'); this.navGroupNode.className = 'nav nav-tabs'; this.node.appendChild(this.navGroupNode); } - - this.node.appendChild(this.buttonGroupNode); } /** From 82f25562dcbd6a03df08659beb77311740db8db2 Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Wed, 19 Feb 2020 12:58:27 +0100 Subject: [PATCH 31/63] Commented code datavisyn/tdp_core#285 --- src/lineup/internal/LineUpPanelActions.ts | 4 ++-- .../internal/panel/PanelAddColumnButton.ts | 12 ++++++++++-- src/lineup/internal/panel/PanelHeader.ts | 19 ++++++++++++------- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/src/lineup/internal/LineUpPanelActions.ts b/src/lineup/internal/LineUpPanelActions.ts index a68b02443..e71e3faca 100644 --- a/src/lineup/internal/LineUpPanelActions.ts +++ b/src/lineup/internal/LineUpPanelActions.ts @@ -151,7 +151,7 @@ export default class LineUpPanelActions extends EventHandler { private init() { const buttons = this.header.buttonGroupNode; - const navs = this.header.navGroupNode; + const navs = this.header.navTabsNode; if (!this.isTopMode && this.options.enableSidePanelCollapsing) { // top mode doesn't need collapse feature const listener = () => { @@ -217,7 +217,7 @@ export default class LineUpPanelActions extends EventHandler { }; const lineupNavButton = new PanelNavButton(header.node, sidePanelTab, 'Lineup Config', 'fa fa-adjust lineup-nav', listener, true); - header.navGroupNode.appendChild(lineupNavButton.node); + header.navTabsNode.appendChild(lineupNavButton.node); } setViolation(violation?: string) { diff --git a/src/lineup/internal/panel/PanelAddColumnButton.ts b/src/lineup/internal/panel/PanelAddColumnButton.ts index adaa1d2ff..1b34758d9 100644 --- a/src/lineup/internal/panel/PanelAddColumnButton.ts +++ b/src/lineup/internal/panel/PanelAddColumnButton.ts @@ -2,13 +2,21 @@ import {SearchBox} from 'lineupjs'; import {ISearchOption} from '../LineUpPanelActions'; import {IPanelButton} from './PanelButton'; import i18n from 'phovea_core/src/i18n'; + +/** + * Div HTMLElement that contains a button and a SearchBox. + * The SearchBox is by default hidden and can bit toggled by the button + */ export default class PanelAddColumnButton implements IPanelButton { readonly node: HTMLElement; - + /** + * + * @param parent The parent HTML DOM element + * @param search LIneup SearchBox instance + */ constructor(parent: HTMLElement, private readonly search: SearchBox) { this.node = parent.ownerDocument.createElement('div'); this.node.classList.add('lu-adder'); - this.node.addEventListener('mouseleave', () => { this.node.classList.remove('once'); }); diff --git a/src/lineup/internal/panel/PanelHeader.ts b/src/lineup/internal/panel/PanelHeader.ts index 71f0f3149..ba8c4da55 100644 --- a/src/lineup/internal/panel/PanelHeader.ts +++ b/src/lineup/internal/panel/PanelHeader.ts @@ -7,20 +7,25 @@ export default class PanelHeader { readonly node: HTMLElement; readonly buttonGroupNode: HTMLElement; - readonly navGroupNode: HTMLElement; + readonly navTabsNode: HTMLElement; private buttons: IPanelButton[] = []; private navTabs: PanelNavButton[] = []; - + /** + * + * @param parent The parent HTML DOM element + * @param isTopMode Is top mode + */ constructor(parent: HTMLElement, isTopMode: boolean) { this.node = parent.ownerDocument.createElement('header'); parent.appendChild(this.node); this.buttonGroupNode = this.node.ownerDocument.createElement('div'); this.buttonGroupNode.classList.add('button-group'); this.node.appendChild(this.buttonGroupNode); + //No nav-tabs when on top mode if (!isTopMode) { - this.navGroupNode = this.node.ownerDocument.createElement('ul'); - this.navGroupNode.className = 'nav nav-tabs'; - this.node.appendChild(this.navGroupNode); + this.navTabsNode = this.node.ownerDocument.createElement('ul'); + this.navTabsNode.className = 'nav nav-tabs'; + this.node.appendChild(this.navTabsNode); } } @@ -34,11 +39,11 @@ export default class PanelHeader { } /** - * Add a PanelNavTab to this header + * Ad nav-tab to the nav-tabs * @param button Panel button instance to add */ addNav(nav: PanelNavButton) { this.navTabs = [...this.navTabs, nav]; - this.navGroupNode.appendChild(nav.node); + this.navTabsNode.appendChild(nav.node); } } From f5773605266fcd2bf9e75017d3929cd364679e52 Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Wed, 19 Feb 2020 13:19:38 +0100 Subject: [PATCH 32/63] Commented code datavisyn/tdp_core#285 --- src/lineup/internal/panel/PanelButton.ts | 5 +++-- src/lineup/internal/panel/PanelDownloadButtonContainer.ts | 4 +--- src/lineup/internal/panel/PanelHeader.ts | 3 ++- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lineup/internal/panel/PanelButton.ts b/src/lineup/internal/panel/PanelButton.ts index 06c59f540..f8f89fd68 100644 --- a/src/lineup/internal/panel/PanelButton.ts +++ b/src/lineup/internal/panel/PanelButton.ts @@ -48,11 +48,12 @@ export class PanelNavButton implements IPanelButton { * @param title String that is used for the title attribute * @param linkClass CSS classes to apply * @param onClick Function that should be executed on button click + * @param defaultNavTab Should PanelNavButton be default active nav-tab */ - constructor(parent: HTMLElement, private readonly tab: PanelTab, title: string, linkClass: string, onClick: () => void, defaultActive?) { + constructor(parent: HTMLElement, private readonly tab: PanelTab, title: string, linkClass: string, onClick: () => void, defaultNavTab?: boolean) { this.node = parent.ownerDocument.createElement('li'); - this.node.className = defaultActive ? 'active' : ' '; + this.node.className = defaultNavTab ? 'active' : ' '; this.node.insertAdjacentHTML('afterbegin', ``); this.node.addEventListener('click', (evt) => { evt.preventDefault(); diff --git a/src/lineup/internal/panel/PanelDownloadButtonContainer.ts b/src/lineup/internal/panel/PanelDownloadButtonContainer.ts index 812d0d29c..33c520f09 100644 --- a/src/lineup/internal/panel/PanelDownloadButtonContainer.ts +++ b/src/lineup/internal/panel/PanelDownloadButtonContainer.ts @@ -5,9 +5,7 @@ import i18n from 'phovea_core/src/i18n'; /** - * An HTML div element tha contains 2 children: - * A button that toggles a dropdown to download select/all rows of the ranking - * A dropwdown HTMLUListElement with download links + * A button dropdown to download selected/all rows of the ranking */ export default class PanelDownloadButtonContainer implements IPanelButton { readonly node: HTMLElement; diff --git a/src/lineup/internal/panel/PanelHeader.ts b/src/lineup/internal/panel/PanelHeader.ts index ba8c4da55..c7ef293b1 100644 --- a/src/lineup/internal/panel/PanelHeader.ts +++ b/src/lineup/internal/panel/PanelHeader.ts @@ -21,6 +21,7 @@ export default class PanelHeader { this.buttonGroupNode = this.node.ownerDocument.createElement('div'); this.buttonGroupNode.classList.add('button-group'); this.node.appendChild(this.buttonGroupNode); + //No nav-tabs when on top mode if (!isTopMode) { this.navTabsNode = this.node.ownerDocument.createElement('ul'); @@ -39,7 +40,7 @@ export default class PanelHeader { } /** - * Ad nav-tab to the nav-tabs + * Add nav-tab to the nav-tabs * @param button Panel button instance to add */ addNav(nav: PanelNavButton) { From 6ed5c778e736513ed893b10397ca9bdeb3ca0f29 Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Wed, 19 Feb 2020 15:00:22 +0100 Subject: [PATCH 33/63] refactor and and comment code datavisyn/tdp_core#285 --- src/lineup/internal/LineUpPanelActions.ts | 22 ++++--- src/lineup/internal/panel/PanelButton.ts | 2 +- .../internal/panel/PanelRankingButton.ts | 4 ++ src/lineup/internal/panel/PanelTab.ts | 48 ++++++++++++++- .../internal/panel/PanelTabContainer.ts | 60 ++++++++++--------- 5 files changed, 94 insertions(+), 42 deletions(-) diff --git a/src/lineup/internal/LineUpPanelActions.ts b/src/lineup/internal/LineUpPanelActions.ts index e71e3faca..018c7ec8b 100644 --- a/src/lineup/internal/LineUpPanelActions.ts +++ b/src/lineup/internal/LineUpPanelActions.ts @@ -123,22 +123,26 @@ export default class LineUpPanelActions extends EventHandler { set collapse(value: boolean) { this.node.classList.toggle('collapsed', value); - if (value && this.options.enableSidePanel !== 'top') { - this.tabContainer.hideCurrentTab(); - } else if (this.options.enableSidePanel !== 'top') { - this.tabContainer.showCurrentTab(); - } + if (value) { + this.tabContainer.hideCurrentTab(); //Hide the active PanelTab --> Inform its content to stop updating + } else { + this.tabContainer.showCurrentTab(); //Show the last active PanelTab --> Inform its content to start updating again + } } hide() { this.node.style.display = 'none'; - this.tabContainer.hideCurrentTab(); //inform the tab content that the sidepanel is collapsed + + //Hide the active PanelTab and inform its content to stop updating + this.tabContainer.hideCurrentTab(); } show() { this.node.style.display = 'flex'; - this.tabContainer.showCurrentTab();//inform the tab content that the sidepanel is collapsed + + //Show the last active PanelTab and inform its content to start updating again + this.tabContainer.showCurrentTab(); } private get isTopMode() { @@ -215,7 +219,7 @@ export default class LineUpPanelActions extends EventHandler { const listener = () => { this.tabContainer.showTab(sidePanelTab); }; - const lineupNavButton = new PanelNavButton(header.node, sidePanelTab, 'Lineup Config', 'fa fa-adjust lineup-nav', listener, true); + const lineupNavButton = new PanelNavButton(header.node, 'Lineup Config', 'fa fa-adjust lineup-nav', listener, true); header.navTabsNode.appendChild(lineupNavButton.node); } @@ -268,7 +272,7 @@ export default class LineUpPanelActions extends EventHandler { }); } }; - return new PanelNavButton(navs, tab, plugin.headerTitle, 'fa ' + plugin.headerCssClass, listener); + return new PanelNavButton(navs, plugin.headerTitle, 'fa ' + plugin.headerCssClass, listener); }); } diff --git a/src/lineup/internal/panel/PanelButton.ts b/src/lineup/internal/panel/PanelButton.ts index f8f89fd68..9a5d1a7f2 100644 --- a/src/lineup/internal/panel/PanelButton.ts +++ b/src/lineup/internal/panel/PanelButton.ts @@ -50,7 +50,7 @@ export class PanelNavButton implements IPanelButton { * @param onClick Function that should be executed on button click * @param defaultNavTab Should PanelNavButton be default active nav-tab */ - constructor(parent: HTMLElement, private readonly tab: PanelTab, title: string, linkClass: string, onClick: () => void, defaultNavTab?: boolean) { + constructor(parent: HTMLElement, title: string, linkClass: string, onClick: () => void, defaultNavTab?: boolean) { this.node = parent.ownerDocument.createElement('li'); this.node.className = defaultNavTab ? 'active' : ' '; diff --git a/src/lineup/internal/panel/PanelRankingButton.ts b/src/lineup/internal/panel/PanelRankingButton.ts index f0b05abf1..be30eb9e2 100644 --- a/src/lineup/internal/panel/PanelRankingButton.ts +++ b/src/lineup/internal/panel/PanelRankingButton.ts @@ -1,6 +1,10 @@ import {IPanelButton} from './PanelButton'; import {LocalDataProvider, Ranking} from 'lineupjs'; +/** + * Plain HTML button with a custom title, CSS class and an onClick function + * Injects through the onClick callback the current ranking + */ export default class PanelRankingButton implements IPanelButton { readonly node: HTMLElement; diff --git a/src/lineup/internal/panel/PanelTab.ts b/src/lineup/internal/panel/PanelTab.ts index 603633654..a078311ba 100644 --- a/src/lineup/internal/panel/PanelTab.ts +++ b/src/lineup/internal/panel/PanelTab.ts @@ -2,10 +2,19 @@ import {SidePanel, SearchBox} from 'lineupjs'; import {ISearchOption} from '../LineUpPanelActions'; import {EventHandler} from 'phovea_core/src/event'; +/** + * Interface for the options parameter of PanelTab + */ export interface IPanelTabDesc { + /** + * Width of the PanelTab (unit is "em") + */ width: string; } +/** + * Events for PanelTab for when its showed/hidden + */ export class PanelTabEvents extends EventHandler { static readonly SHOW_PANEL = 'showPanel'; @@ -14,11 +23,19 @@ export class PanelTabEvents extends EventHandler { } +/** + * The PanelTab creates a tab component that with can be toggled through the PanelNavButton + * Exposes `show()`, `hide()`, `isClosed()` methods + */ export class PanelTab { readonly node: HTMLElement; readonly events: PanelTabEvents; - + /** + * + * @param parent The parent HTML DOM element + * @param options Extra styles to apply to the PanelTab + */ constructor(parent: HTMLElement, options?: IPanelTabDesc) { this.events = new PanelTabEvents(); @@ -28,26 +45,51 @@ export class PanelTab { const o = Object.assign({}, options); this.node.style.width = o.width + 'em' || null; } - + /** + * Show self/ add active class + * Fire `SHOW_PANEL` event + */ public show() { this.node.classList.add('active'); this.events.fire(PanelTabEvents.SHOW_PANEL); } + /** + * Hide self/ remove active class + * Fire `HIDE_PANEL` event + */ public hide() { this.node.classList.remove('active'); this.events.fire(PanelTabEvents.HIDE_PANEL); } - + /** + * Is the tab active/open + */ public isClosed() { return !this.node.classList.contains('active'); } + /** + * Is currentTab default active/ open tab + */ + isDefault() { + return !this.node.classList.contains('default'); + } } +/** + * Default active PanelTab + * Contains LineUp SidePanel and LineUp SearchBox + */ export class SidePanelTab extends PanelTab { readonly panel: SidePanel | null; + /** + * @param parent The parent HTML DOM element + * @param search LineUp SearchBox + * @param ctx Ctx + * @param doc Document + */ constructor(parent: HTMLElement, private readonly search: SearchBox, ctx: any, doc = document) { super(parent); this.node.classList.add('default'); diff --git a/src/lineup/internal/panel/PanelTabContainer.ts b/src/lineup/internal/panel/PanelTabContainer.ts index 1ddc5c5b1..3836f1052 100644 --- a/src/lineup/internal/panel/PanelTabContainer.ts +++ b/src/lineup/internal/panel/PanelTabContainer.ts @@ -1,5 +1,9 @@ import {PanelTab} from './PanelTab'; +/** + * The PanelTabContainer contains an array of PanelTabs + * Exposes methods to toggle between PanelTabs + */ export default class PanelTabContainer { readonly node: HTMLElement; @@ -8,38 +12,37 @@ export default class PanelTabContainer { private currentTab: PanelTab; + /** + * @param parent The parent HTML DOM element + */ constructor(parent: HTMLElement) { this.node = parent.ownerDocument.createElement('main'); this.node.classList.add('tab-content'); parent.appendChild(this.node); } + /** + * Find default/active tab + * @returns A PanelTab instance + */ private get defaultTab(): PanelTab { - return this.tabs[0]; + return this.tabs.find((tab) => tab.isDefault()); } - addTab(tab: PanelTab) { + /** + * Method to add a new PanelTab + * @param tab New PanelTab instance + */ + public addTab(tab: PanelTab) { this.tabs = [...this.tabs, tab]; this.node.appendChild(tab.node); } - toggleTab(tab: PanelTab) { - if (this.currentTab === tab) { - this.hideTab(tab); - - } else { - this.showTab(tab); - } - } - - showDefault() { - const openedTab = this.tabs.find((tab) => tab.node.classList.contains('tab-pane') && tab.node.classList.contains('active')); - if (openedTab) { - this.hideTab(openedTab); - } - } - - showTab(tab: PanelTab) { + /** + * Close currentTab and show new PanelTab + * @param tab A PanelTab instance + */ + public showTab(tab: PanelTab) { if (this.currentTab) { this.currentTab.hide(); } @@ -47,18 +50,17 @@ export default class PanelTabContainer { tab.show(); this.currentTab = tab; } - - hideTab(tab: PanelTab) { - tab.hide(); - this.defaultTab.show(); - this.currentTab = this.defaultTab; - } - - showCurrentTab() { + /** + * Show last opened PanelTab + * Used when the LineUpPanelActions reopens to show the last open PanelTab + */ + public showCurrentTab() { this.currentTab.show(); } - - hideCurrentTab() { + /** + * Hide currentTab + */ + public hideCurrentTab() { this.currentTab.hide(); } } From 65eedd94e3df7abed3258ae550912a4abdab3410 Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Wed, 19 Feb 2020 15:23:47 +0100 Subject: [PATCH 34/63] refactor & comment code datavisyn/tdp_core#285 --- src/lineup/internal/LineUpPanelActions.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/lineup/internal/LineUpPanelActions.ts b/src/lineup/internal/LineUpPanelActions.ts index 018c7ec8b..94626c931 100644 --- a/src/lineup/internal/LineUpPanelActions.ts +++ b/src/lineup/internal/LineUpPanelActions.ts @@ -92,7 +92,7 @@ export default class LineUpPanelActions extends EventHandler { } else { const sidePanel = new SidePanelTab(this.node, this.searchBoxProvider.createSearchBox(), ctx, doc); this.panel = sidePanel.panel; - this.appendLineUpNav(this.header, sidePanel); + this.appendLineUpPanelNavButton(this.header, sidePanel); this.tabContainer = new PanelTabContainer(this.node); this.tabContainer.addTab(sidePanel); this.tabContainer.showTab(sidePanel); @@ -215,12 +215,15 @@ export default class LineUpPanelActions extends EventHandler { } } - appendLineUpNav(header, sidePanelTab: PanelTab) { + /** + * Append LineUp PanelNavButton to the nav-tabs + */ + appendLineUpPanelNavButton(header: PanelHeader, sidePanelTab: PanelTab) { const listener = () => { this.tabContainer.showTab(sidePanelTab); }; - const lineupNavButton = new PanelNavButton(header.node, 'Lineup Config', 'fa fa-adjust lineup-nav', listener, true); + const lineupNavButton = new PanelNavButton(header.node, 'Lineup Config', 'fa fa-adjust lineup-nav', listener, true); header.navTabsNode.appendChild(lineupNavButton.node); } From f46f630318496a72c24055b89b8b9100183857e7 Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Wed, 19 Feb 2020 15:47:24 +0100 Subject: [PATCH 35/63] commneted code datavisyn/tdp_core#257 --- src/lineup/internal/utils.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lineup/internal/utils.ts b/src/lineup/internal/utils.ts index 1e7a267fa..c263dcc1f 100644 --- a/src/lineup/internal/utils.ts +++ b/src/lineup/internal/utils.ts @@ -8,7 +8,9 @@ import {convertRow2MultiMap, IFormMultiMap, IFormRow} from '../../form'; import {encodeParams} from 'phovea_core/src/ajax'; - +/** + * Interface for AScoreAccessorProxy + */ export interface IAccessorFunc { (row: IDataRow): T; } From e7b41035afd0939c3ca4ccda332889948b1674a4 Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Wed, 19 Feb 2020 16:20:24 +0100 Subject: [PATCH 36/63] fix bug datavisyn/tdp_core#285 --- src/lineup/internal/LineUpPanelActions.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/lineup/internal/LineUpPanelActions.ts b/src/lineup/internal/LineUpPanelActions.ts index 94626c931..b36b472b1 100644 --- a/src/lineup/internal/LineUpPanelActions.ts +++ b/src/lineup/internal/LineUpPanelActions.ts @@ -124,9 +124,10 @@ export default class LineUpPanelActions extends EventHandler { set collapse(value: boolean) { this.node.classList.toggle('collapsed', value); - if (value) { + //When this.options.enableSidePanel === 'top' `this.collapsed=true` gets called. Attempting to open the PanelTab produces an error. + if (value && this.options.enableSidePanel !== 'top') { this.tabContainer.hideCurrentTab(); //Hide the active PanelTab --> Inform its content to stop updating - } else { + } else if (this.options.enableSidePanel !== 'top') { this.tabContainer.showCurrentTab(); //Show the last active PanelTab --> Inform its content to start updating again } } From 537b5d0d968daf829a819c1dc1d47c907c9b0bb9 Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Fri, 28 Feb 2020 14:49:53 +0100 Subject: [PATCH 37/63] * Restructured the LineUpPanelActions components * Refactored and improved styles * Added order property to extension point datavisyn/tdp_core#285 --- src/extensions.ts | 16 +-- src/lineup/internal/LineUpPanelActions.ts | 68 ++++++------ src/lineup/internal/panel/PanelButton.ts | 26 +++-- src/lineup/internal/panel/PanelHeader.ts | 29 +---- src/lineup/internal/panel/PanelTab.ts | 54 ++++++---- .../internal/panel/PanelTabContainer.ts | 80 ++++++++++++-- src/styles/_view_lineup.scss | 101 ++++++++++-------- 7 files changed, 226 insertions(+), 148 deletions(-) diff --git a/src/extensions.ts b/src/extensions.ts index 03edfed58..b96726973 100644 --- a/src/extensions.ts +++ b/src/extensions.ts @@ -154,13 +154,17 @@ export interface IPanelTabExtension { export interface IPanelTabExtensionDesc extends IPluginDesc { /** - * @param headerCssClass css class for the button/header of the LineupSidePanel tab - * @param headerTitle title of the above button/header - * @param tabDesc description of the tab e.g { width:'40' } + * @param CssClass css class for the button/header of the LineupSidePanel tab + * @param title title of the above button/header + * @param order position of tab `0 , 10, 20, ...` to order the tabs + * @param width width of the sidePanel + * @param shortcut add button in collapsed mode to access PanelTab @default false */ - headerCssClass: string; - headerTitle: string; - tabDesc: IPanelTabDesc; + cssClass: string; + title: string; + order: number; + width: string; + shortcut?: boolean; load(): Promise; } diff --git a/src/lineup/internal/LineUpPanelActions.ts b/src/lineup/internal/LineUpPanelActions.ts index 94626c931..6da732757 100644 --- a/src/lineup/internal/LineUpPanelActions.ts +++ b/src/lineup/internal/LineUpPanelActions.ts @@ -12,7 +12,7 @@ import {IARankingViewOptions} from '../ARankingView'; import {lazyDialogModule} from '../../dialogs'; import PanelButton, {IPanelButton, PanelNavButton} from './panel/PanelButton'; import PanelTabContainer from './panel/PanelTabContainer'; -import {PanelTab, SidePanelTab, IPanelTabDesc} from './panel/PanelTab'; +import {PanelTab, SidePanelTab} from './panel/PanelTab'; import SearchBoxProvider from './panel/SearchBoxProvider'; import PanelHeader from './panel/PanelHeader'; import PanelRankingButton from './panel/PanelRankingButton'; @@ -32,6 +32,7 @@ export const rule = spaceFillingRule({ groupPadding: 5 }); + /** * Wraps the score such that the plugin is loaded and the score modal opened, when the factory function is called * @param score @@ -78,23 +79,33 @@ export default class LineUpPanelActions extends EventHandler { constructor(protected readonly provider: LocalDataProvider, ctx: any, private readonly options: Readonly, doc = document) { super(); - this.node = doc.createElement('aside'); this.node.classList.add('lu-side-panel-wrapper'); - this.header = new PanelHeader(this.node, this.options.enableSidePanel === 'top'); + this.header = new PanelHeader(this.node); this.searchBoxProvider = new SearchBoxProvider(); if (this.options.enableSidePanel === 'top') { this.node.classList.add('lu-side-panel-top'); - } else { - const sidePanel = new SidePanelTab(this.node, this.searchBoxProvider.createSearchBox(), ctx, doc); + + const options = { + cssClass: 'fa fa-sliders', + title: 'Ranking Configuration', + width: '21em', + order: 0 + }; + + const sidePanel = new SidePanelTab(this.node, this.searchBoxProvider.createSearchBox(), ctx, doc, options); this.panel = sidePanel.panel; - this.appendLineUpPanelNavButton(this.header, sidePanel); + + const listener = () => { + this.tabContainer.showTab(sidePanel); + }; this.tabContainer = new PanelTabContainer(this.node); - this.tabContainer.addTab(sidePanel); + const lineupNavButton = new PanelNavButton(this.tabContainer.node, listener, options, true); + this.tabContainer.addTab(sidePanel, lineupNavButton); this.tabContainer.showTab(sidePanel); } @@ -154,8 +165,7 @@ export default class LineUpPanelActions extends EventHandler { } private init() { - const buttons = this.header.buttonGroupNode; - const navs = this.header.navTabsNode; + const buttons = this.header.node; if (!this.isTopMode && this.options.enableSidePanelCollapsing) { // top mode doesn't need collapse feature const listener = () => { @@ -209,24 +219,10 @@ export default class LineUpPanelActions extends EventHandler { } if (!this.isTopMode) { - this.appendExtraTabs(navs).forEach((nav: PanelNavButton) => { - this.header.addNav(nav); - }); + this.appendExtraTabs(); } } - /** - * Append LineUp PanelNavButton to the nav-tabs - */ - appendLineUpPanelNavButton(header: PanelHeader, sidePanelTab: PanelTab) { - const listener = () => { - this.tabContainer.showTab(sidePanelTab); - }; - - const lineupNavButton = new PanelNavButton(header.node, 'Lineup Config', 'fa fa-adjust lineup-nav', listener, true); - header.navTabsNode.appendChild(lineupNavButton.node); - } - setViolation(violation?: string) { if (violation) { this.overview.dataset.violation = violation; @@ -247,13 +243,11 @@ export default class LineUpPanelActions extends EventHandler { }); } - private appendExtraTabs(navs: HTMLElement) { + private appendExtraTabs() { const plugins = listPlugins(EP_TDP_CORE_LINEUP_PANEL_TAB); - return plugins.map((plugin) => { - const tab = new PanelTab(this.tabContainer.node, plugin.tabDesc); - this.tabContainer.addTab(tab); - + plugins.forEach((plugin) => { let isLoaded = false; + const tab = new PanelTab(this.tabContainer.node, plugin); const listener = () => { if (isLoaded) { @@ -264,18 +258,28 @@ export default class LineUpPanelActions extends EventHandler { } else { this.tabContainer.showTab(tab); } - } else { plugin.load().then((p) => { p.factory(tab.node, this.provider, p.desc, tab.events); this.collapse = false; // expand side panel this.tabContainer.showTab(tab); - isLoaded = true; }); } }; - return new PanelNavButton(navs, plugin.headerTitle, 'fa ' + plugin.headerCssClass, listener); + const nav = new PanelNavButton(this.tabContainer.node, listener, plugin); + // if shortcut===true create button on the header to open tab + // manually add the `active` class to the corresponding PanelNavButton + if (plugin.shortcut) { + + const onClick = () => { + listener(); + nav.setActive(); + }; + const button = new PanelButton(this.header.node, plugin.title, 'fa ' + plugin.cssClass + ' shortcut-nav', onClick); + this.header.addButton(button); + } + this.tabContainer.addTab(tab, nav); }); } diff --git a/src/lineup/internal/panel/PanelButton.ts b/src/lineup/internal/panel/PanelButton.ts index 9a5d1a7f2..a33d7a074 100644 --- a/src/lineup/internal/panel/PanelButton.ts +++ b/src/lineup/internal/panel/PanelButton.ts @@ -1,4 +1,4 @@ -import {PanelTab} from './PanelTab'; +import {PanelTab, IPanelTabDesc} from './PanelTab'; /** * Interface for the LineUp panel button */ @@ -40,24 +40,34 @@ export default class PanelButton implements IPanelButton { */ export class PanelNavButton implements IPanelButton { readonly node: HTMLElement; + readonly order: number; /** * Constructor of the PanelButton * @param parent The parent HTML DOM element - * @param tab The tab it is connected to - * @param title String that is used for the title attribute - * @param linkClass CSS classes to apply * @param onClick Function that should be executed on button click - * @param defaultNavTab Should PanelNavButton be default active nav-tab + * @param setParentWidth callback to pass set the width of the parent + * @param options Options to customize the PanelNavButton + * @param defaultNavTab Should this PanelNavButton be the default active navButton */ - constructor(parent: HTMLElement, title: string, linkClass: string, onClick: () => void, defaultNavTab?: boolean) { - + constructor(parent: HTMLElement, onClick: () => void, options: IPanelTabDesc, defaultNavTab?: boolean) { this.node = parent.ownerDocument.createElement('li'); this.node.className = defaultNavTab ? 'active' : ' '; - this.node.insertAdjacentHTML('afterbegin', ``); + this.node.insertAdjacentHTML('afterbegin', ` ${options.title || ''}`); this.node.addEventListener('click', (evt) => { evt.preventDefault(); onClick(); }); } + + /** + * When you click the shortcut button in collapsed mode focus on the navButton + */ + setActive() { + const navButtons = Array.from(this.node.parentElement.children); + for (const nav of navButtons) { + nav.classList.remove('active'); + } + this.node.classList.add('active'); + } } diff --git a/src/lineup/internal/panel/PanelHeader.ts b/src/lineup/internal/panel/PanelHeader.ts index c7ef293b1..8e975a75d 100644 --- a/src/lineup/internal/panel/PanelHeader.ts +++ b/src/lineup/internal/panel/PanelHeader.ts @@ -1,4 +1,4 @@ -import {IPanelButton, PanelNavButton} from './PanelButton'; +import {IPanelButton} from './PanelButton'; /** * The panel header contains a list of panel buttons. @@ -6,28 +6,16 @@ import {IPanelButton, PanelNavButton} from './PanelButton'; export default class PanelHeader { readonly node: HTMLElement; - readonly buttonGroupNode: HTMLElement; - readonly navTabsNode: HTMLElement; private buttons: IPanelButton[] = []; - private navTabs: PanelNavButton[] = []; + /** * * @param parent The parent HTML DOM element * @param isTopMode Is top mode */ - constructor(parent: HTMLElement, isTopMode: boolean) { + constructor(parent: HTMLElement) { this.node = parent.ownerDocument.createElement('header'); parent.appendChild(this.node); - this.buttonGroupNode = this.node.ownerDocument.createElement('div'); - this.buttonGroupNode.classList.add('button-group'); - this.node.appendChild(this.buttonGroupNode); - - //No nav-tabs when on top mode - if (!isTopMode) { - this.navTabsNode = this.node.ownerDocument.createElement('ul'); - this.navTabsNode.className = 'nav nav-tabs'; - this.node.appendChild(this.navTabsNode); - } } /** @@ -36,15 +24,6 @@ export default class PanelHeader { */ addButton(button: IPanelButton) { this.buttons = [...this.buttons, button]; - this.buttonGroupNode.appendChild(button.node); - } - - /** - * Add nav-tab to the nav-tabs - * @param button Panel button instance to add - */ - addNav(nav: PanelNavButton) { - this.navTabs = [...this.navTabs, nav]; - this.navTabsNode.appendChild(nav.node); + this.node.appendChild(button.node); } } diff --git a/src/lineup/internal/panel/PanelTab.ts b/src/lineup/internal/panel/PanelTab.ts index a078311ba..a98e19295 100644 --- a/src/lineup/internal/panel/PanelTab.ts +++ b/src/lineup/internal/panel/PanelTab.ts @@ -6,10 +6,32 @@ import {EventHandler} from 'phovea_core/src/event'; * Interface for the options parameter of PanelTab */ export interface IPanelTabDesc { + /** - * Width of the PanelTab (unit is "em") + * Width of the SidePanel */ width: string; + + /** + * Css class for PanelNavButton of the PanelTab + */ + cssClass: string; + + /** + * Title and Text content for the PanelNavButton of the PanelTab. + */ + title: string; + + /** + * Used to sort the PanelNavButtons + */ + order: number; + + /** + * Show PanelNavButton in collapsed mode + * @default false + */ + shortcut?: boolean; } /** @@ -25,29 +47,28 @@ export class PanelTabEvents extends EventHandler { /** * The PanelTab creates a tab component that with can be toggled through the PanelNavButton - * Exposes `show()`, `hide()`, `isClosed()` methods */ export class PanelTab { readonly node: HTMLElement; readonly events: PanelTabEvents; + readonly width: string; /** - * + * @param * @param parent The parent HTML DOM element * @param options Extra styles to apply to the PanelTab */ - constructor(parent: HTMLElement, options?: IPanelTabDesc) { + constructor(parent: HTMLElement, options: IPanelTabDesc) { this.events = new PanelTabEvents(); - this.node = parent.ownerDocument.createElement('div'); this.node.classList.add('tab-pane'); - + this.node.setAttribute('role', 'tabpanel'); const o = Object.assign({}, options); - this.node.style.width = o.width + 'em' || null; + this.width = o.width; } + /** * Show self/ add active class - * Fire `SHOW_PANEL` event */ public show() { this.node.classList.add('active'); @@ -56,24 +77,11 @@ export class PanelTab { /** * Hide self/ remove active class - * Fire `HIDE_PANEL` event */ public hide() { this.node.classList.remove('active'); this.events.fire(PanelTabEvents.HIDE_PANEL); } - /** - * Is the tab active/open - */ - public isClosed() { - return !this.node.classList.contains('active'); - } - /** - * Is currentTab default active/ open tab - */ - isDefault() { - return !this.node.classList.contains('default'); - } } /** @@ -90,8 +98,8 @@ export class SidePanelTab extends PanelTab { * @param ctx Ctx * @param doc Document */ - constructor(parent: HTMLElement, private readonly search: SearchBox, ctx: any, doc = document) { - super(parent); + constructor(parent: HTMLElement, private readonly search: SearchBox, ctx: any, doc = document, options: IPanelTabDesc) { + super(parent, options); this.node.classList.add('default'); this.panel = new SidePanel(ctx, doc, { chooser: false diff --git a/src/lineup/internal/panel/PanelTabContainer.ts b/src/lineup/internal/panel/PanelTabContainer.ts index 3836f1052..d58d33483 100644 --- a/src/lineup/internal/panel/PanelTabContainer.ts +++ b/src/lineup/internal/panel/PanelTabContainer.ts @@ -1,41 +1,97 @@ import {PanelTab} from './PanelTab'; +import {PanelNavButton} from './PanelButton'; + + + +/** + * Acts as the navigation to the TabPanels + * Allows the addition in the correct order of PanelNavButtons + */ +class PanelTabHeader { + public node: HTMLElement; + public navButtons: PanelNavButton[] = []; + + /** + * @param parent The parent HTML DOM element + */ + constructor(parent: HTMLElement) { + this.node = parent.ownerDocument.createElement('ul'); + this.node.className = 'nav nav-tabs'; + parent.appendChild(this.node); + } + + /** + * + * @param navTabs NavTabs array sorted according to their order property + * @param nav New nav being added to the NavTabs + */ + getNextSibling(sortedNavTabs: PanelNavButton[], nav: PanelNavButton): PanelNavButton | null { + const navTabsLength = sortedNavTabs.length; + const navIndex = sortedNavTabs.findIndex((n) => n.order === nav.order); + return navIndex === navTabsLength - 1 ? null : sortedNavTabs[navIndex + 1]; + } + + /** + * Add nav-tab to the nav-tabs in the correct order. + * @param button PanelNavButton instance to add + */ + addNav(nav: PanelNavButton) { + const sameOrderNav = this.navButtons.find((n) => n.order === nav.order); // find nav with same order + this.navButtons = [...this.navButtons, nav].sort((nav1, nav2) => nav1.order - nav2.order); + this.node.appendChild(nav.node); + const nextSibling = sameOrderNav || this.getNextSibling(this.navButtons, nav); // if same order property append new nav before it + if (nextSibling) { + this.node.insertBefore(nav.node, nextSibling.node); + } else { + this.node.appendChild(nav.node); + } + } +} + /** - * The PanelTabContainer contains an array of PanelTabs - * Exposes methods to toggle between PanelTabs + * The PanelTabContainer creates tab able nav buttons that toggle their corresponding PanelTab */ export default class PanelTabContainer { readonly node: HTMLElement; - + private parent: HTMLElement; + readonly tabContentNode: HTMLElement; private tabs: PanelTab[] = []; - + private tabHeader: PanelTabHeader; private currentTab: PanelTab; + /** * @param parent The parent HTML DOM element */ constructor(parent: HTMLElement) { + this.parent = parent; this.node = parent.ownerDocument.createElement('main'); - this.node.classList.add('tab-content'); + this.tabContentNode = this.node.ownerDocument.createElement('div'); + + this.tabContentNode.classList.add('tab-content'); + this.tabHeader = new PanelTabHeader(this.node); + this.node.appendChild(this.tabContentNode); parent.appendChild(this.node); } /** - * Find default/active tab - * @returns A PanelTab instance + * Resize the Panel to fit the content of the new tab + * @param width width the PanelTabContainer should have; */ - private get defaultTab(): PanelTab { - return this.tabs.find((tab) => tab.isDefault()); + resizeNode(width: string) { + this.parent.style.width = width; } /** * Method to add a new PanelTab * @param tab New PanelTab instance */ - public addTab(tab: PanelTab) { + public addTab(tab: PanelTab, nav: PanelNavButton) { this.tabs = [...this.tabs, tab]; - this.node.appendChild(tab.node); + this.tabHeader.addNav(nav); + this.tabContentNode.appendChild(tab.node); } /** @@ -47,9 +103,11 @@ export default class PanelTabContainer { this.currentTab.hide(); } + this.resizeNode(tab.width); tab.show(); this.currentTab = tab; } + /** * Show last opened PanelTab * Used when the LineUpPanelActions reopens to show the last open PanelTab diff --git a/src/styles/_view_lineup.scss b/src/styles/_view_lineup.scss index a8924fb4a..b92610c60 100644 --- a/src/styles/_view_lineup.scss +++ b/src/styles/_view_lineup.scss @@ -130,6 +130,12 @@ $lu_assets: "~lineupjs/src/assets"; } } + &:not(.collapsed) { + .shortcut-nav { + display: none; // hide navTab that doesn't have class `shortcut-nav` in collapsed mode + } + } + .lu-side-panel > main > section > div { flex: 1; } @@ -139,24 +145,9 @@ $lu_assets: "~lineupjs/src/assets"; } > header { - & > :not(.collapsed) { - margin-bottom: 1em; - } - - .button-group { - display: flex; - margin-bottom: 0.5em; - } - - ul.nav-tabs { - display: flex; - - & > li > a { - color: black; - padding: 5px 8.5px; - cursor: pointer; - } - } + display: flex; + flex-wrap: wrap; + margin-bottom: 0.5em; .lu-adder { margin-top: 1em; @@ -186,11 +177,6 @@ $lu_assets: "~lineupjs/src/assets"; } } - .tab-content { - display: flex; - flex-direction: column; - } - .collapse-button { @extend %lu-panel-button; grid-row: 1/3; @@ -237,9 +223,10 @@ $lu_assets: "~lineupjs/src/assets"; } .download-data-dropdown { + // show the number of selected rows after the element li[data-num-selected-rows]::after { - content: " (" attr(data-num-selected-rows) ")"; + content: " ("attr(data-num-selected-rows) ")"; display: inline; } @@ -251,6 +238,41 @@ $lu_assets: "~lineupjs/src/assets"; } } + > main { + display: flex; + flex-direction: column; + flex: 1; + + ul.nav-tabs { + display: flex; + + & > li { + &:only-child() { + display: none; + } + + > a { + color: black; + padding: 5px 8.5px; + cursor: pointer; + text-align-last: center; + } + } + } + + .tab-content { + margin-top: 0.5em; + display: flex; + flex-direction: column; + flex: 1; + overflow: auto; + } + + .tab-pane.default { + overflow: hidden; + } + } + &.collapsed { max-width: 3em; min-width: 0; @@ -268,30 +290,23 @@ $lu_assets: "~lineupjs/src/assets"; } > header { + // Add margin before the first shortcut-nav + + > button:not(.shortcut-nav) + .shortcut-nav { + margin-top: 1em; + } + margin-right: 0; min-width: 0; align-self: flex-start; - width: auto; + max-width: 100%; flex-direction: column; - .button-group { - display: flex; - flex-direction: column; - } - .nav-tabs { - margin-top: 1em; - li { - > a { - margin-right: 0px; - @extend %lu-panel-button; - } - } - .lineup-nav { - display: none; //hide lineup tab when sidePanel is collapsed - } - } + display: flex; + flex-direction: column; } - .tab-content { + + > main { display: none; } @@ -355,7 +370,7 @@ $lu_assets: "~lineupjs/src/assets"; float: right; border-left: none; - > header > .button-group { + > header { display: flex; flex-direction: row; max-width: unset; From e14210a2d3db467753b30168aca2adf2f795e830 Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Fri, 28 Feb 2020 15:16:38 +0100 Subject: [PATCH 38/63] added order property to PanelNavButton datavisyn/tdp_core#285 --- src/lineup/internal/panel/PanelButton.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lineup/internal/panel/PanelButton.ts b/src/lineup/internal/panel/PanelButton.ts index a33d7a074..434318f0d 100644 --- a/src/lineup/internal/panel/PanelButton.ts +++ b/src/lineup/internal/panel/PanelButton.ts @@ -53,6 +53,7 @@ export class PanelNavButton implements IPanelButton { constructor(parent: HTMLElement, onClick: () => void, options: IPanelTabDesc, defaultNavTab?: boolean) { this.node = parent.ownerDocument.createElement('li'); this.node.className = defaultNavTab ? 'active' : ' '; + this.order = options.order; this.node.insertAdjacentHTML('afterbegin', ` ${options.title || ''}`); this.node.addEventListener('click', (evt) => { evt.preventDefault(); From ee0be72e4ea67101da427632292bfe2a91a04dbd Mon Sep 17 00:00:00 2001 From: Holger Stitz Date: Mon, 2 Mar 2020 16:26:03 +0100 Subject: [PATCH 39/63] Refactor tab, nav, and shortcut button --- src/lineup/internal/LineUpPanelActions.ts | 29 +++++-------------- src/lineup/internal/panel/PanelButton.ts | 27 ++++++++--------- src/lineup/internal/panel/PanelTab.ts | 23 ++++++++++++++- .../internal/panel/PanelTabContainer.ts | 29 ++++++++++++------- 4 files changed, 63 insertions(+), 45 deletions(-) diff --git a/src/lineup/internal/LineUpPanelActions.ts b/src/lineup/internal/LineUpPanelActions.ts index 639709162..6a2c6cb60 100644 --- a/src/lineup/internal/LineUpPanelActions.ts +++ b/src/lineup/internal/LineUpPanelActions.ts @@ -100,12 +100,8 @@ export default class LineUpPanelActions extends EventHandler { const sidePanel = new SidePanelTab(this.node, this.searchBoxProvider.createSearchBox(), ctx, doc, options); this.panel = sidePanel.panel; - const listener = () => { - this.tabContainer.showTab(sidePanel); - }; this.tabContainer = new PanelTabContainer(this.node); - const lineupNavButton = new PanelNavButton(this.tabContainer.node, listener, options, true); - this.tabContainer.addTab(sidePanel, lineupNavButton); + this.tabContainer.addTab(sidePanel); this.tabContainer.showTab(sidePanel); } @@ -250,15 +246,13 @@ export default class LineUpPanelActions extends EventHandler { let isLoaded = false; const tab = new PanelTab(this.tabContainer.node, plugin); - const listener = () => { + const onClick = () => { if (isLoaded) { if (this.collapse) { this.collapse = false; // expand side panel - this.tabContainer.showTab(tab); - - } else { - this.tabContainer.showTab(tab); } + this.tabContainer.showTab(tab); + } else { plugin.load().then((p) => { p.factory(tab.node, this.provider, p.desc, tab.events); @@ -268,19 +262,12 @@ export default class LineUpPanelActions extends EventHandler { }); } }; - const nav = new PanelNavButton(this.tabContainer.node, listener, plugin); - // if shortcut===true create button on the header to open tab - // manually add the `active` class to the corresponding PanelNavButton - if (plugin.shortcut) { - const onClick = () => { - listener(); - nav.setActive(); - }; - const button = new PanelButton(this.header.node, plugin.title, 'fa ' + plugin.cssClass + ' shortcut-nav', onClick); - this.header.addButton(button); + if (plugin.shortcut) { + this.header.addButton(tab.getShortcutButton()); } - this.tabContainer.addTab(tab, nav); + + this.tabContainer.addTab(tab, onClick); }); } diff --git a/src/lineup/internal/panel/PanelButton.ts b/src/lineup/internal/panel/PanelButton.ts index 434318f0d..4901aeb42 100644 --- a/src/lineup/internal/panel/PanelButton.ts +++ b/src/lineup/internal/panel/PanelButton.ts @@ -46,29 +46,30 @@ export class PanelNavButton implements IPanelButton { * Constructor of the PanelButton * @param parent The parent HTML DOM element * @param onClick Function that should be executed on button click - * @param setParentWidth callback to pass set the width of the parent * @param options Options to customize the PanelNavButton - * @param defaultNavTab Should this PanelNavButton be the default active navButton */ - constructor(parent: HTMLElement, onClick: () => void, options: IPanelTabDesc, defaultNavTab?: boolean) { + constructor(parent: HTMLElement, onClick: () => void, options: IPanelTabDesc) { this.node = parent.ownerDocument.createElement('li'); - this.node.className = defaultNavTab ? 'active' : ' '; this.order = options.order; - this.node.insertAdjacentHTML('afterbegin', ` ${options.title || ''}`); - this.node.addEventListener('click', (evt) => { + this.node.insertAdjacentHTML('afterbegin', ` ${options.title || ''}`); + this.node.querySelector('a').addEventListener('click', (evt) => { evt.preventDefault(); onClick(); }); } /** - * When you click the shortcut button in collapsed mode focus on the navButton + * Set the active class to this button + * @param isActive Toggle the class + */ + setActive(isActive: boolean) { + this.node.classList.toggle('active', isActive); + } + + /** + * Trigger click event on anchor element. */ - setActive() { - const navButtons = Array.from(this.node.parentElement.children); - for (const nav of navButtons) { - nav.classList.remove('active'); - } - this.node.classList.add('active'); + click() { + this.node.querySelector('a').click(); } } diff --git a/src/lineup/internal/panel/PanelTab.ts b/src/lineup/internal/panel/PanelTab.ts index a98e19295..65374c137 100644 --- a/src/lineup/internal/panel/PanelTab.ts +++ b/src/lineup/internal/panel/PanelTab.ts @@ -1,6 +1,7 @@ import {SidePanel, SearchBox} from 'lineupjs'; import {ISearchOption} from '../LineUpPanelActions'; import {EventHandler} from 'phovea_core/src/event'; +import PanelButton, {PanelNavButton} from './PanelButton'; /** * Interface for the options parameter of PanelTab @@ -53,12 +54,15 @@ export class PanelTab { readonly node: HTMLElement; readonly events: PanelTabEvents; readonly width: string; + + private navButton: PanelNavButton; + /** * @param * @param parent The parent HTML DOM element * @param options Extra styles to apply to the PanelTab */ - constructor(parent: HTMLElement, options: IPanelTabDesc) { + constructor(parent: HTMLElement, private options: IPanelTabDesc) { this.events = new PanelTabEvents(); this.node = parent.ownerDocument.createElement('div'); this.node.classList.add('tab-pane'); @@ -72,6 +76,7 @@ export class PanelTab { */ public show() { this.node.classList.add('active'); + this.navButton.setActive(true); this.events.fire(PanelTabEvents.SHOW_PANEL); } @@ -80,8 +85,24 @@ export class PanelTab { */ public hide() { this.node.classList.remove('active'); + this.navButton.setActive(false); this.events.fire(PanelTabEvents.HIDE_PANEL); } + + getNavButton(listener): PanelNavButton { + // Note: `document.body` is used only for `parent.ownerDocument.createElement()` inside the button + this.navButton = new PanelNavButton(document.body, listener, this.options); + return this.navButton; + } + + getShortcutButton(): PanelButton { + const onClick = () => { + this.navButton.click(); + }; + + // Note: `document.body` is used only for `parent.ownerDocument.createElement()` inside the button + return new PanelButton(document.body, this.options.title, 'fa ' + this.options.cssClass + ' shortcut-nav', onClick); + } } /** diff --git a/src/lineup/internal/panel/PanelTabContainer.ts b/src/lineup/internal/panel/PanelTabContainer.ts index d58d33483..94b1c7aa9 100644 --- a/src/lineup/internal/panel/PanelTabContainer.ts +++ b/src/lineup/internal/panel/PanelTabContainer.ts @@ -35,15 +35,15 @@ class PanelTabHeader { * Add nav-tab to the nav-tabs in the correct order. * @param button PanelNavButton instance to add */ - addNav(nav: PanelNavButton) { - const sameOrderNav = this.navButtons.find((n) => n.order === nav.order); // find nav with same order - this.navButtons = [...this.navButtons, nav].sort((nav1, nav2) => nav1.order - nav2.order); - this.node.appendChild(nav.node); - const nextSibling = sameOrderNav || this.getNextSibling(this.navButtons, nav); // if same order property append new nav before it + addNavButton(button: PanelNavButton) { + const sameOrderNav = this.navButtons.find((n) => n.order === button.order); // find nav with same order + this.navButtons = [...this.navButtons, button].sort((nav1, nav2) => nav1.order - nav2.order); + this.node.appendChild(button.node); + const nextSibling = sameOrderNav || this.getNextSibling(this.navButtons, button); // if same order property append new nav before it if (nextSibling) { - this.node.insertBefore(nav.node, nextSibling.node); + this.node.insertBefore(button.node, nextSibling.node); } else { - this.node.appendChild(nav.node); + this.node.appendChild(button.node); } } } @@ -68,10 +68,12 @@ export default class PanelTabContainer { constructor(parent: HTMLElement) { this.parent = parent; this.node = parent.ownerDocument.createElement('main'); - this.tabContentNode = this.node.ownerDocument.createElement('div'); + this.tabContentNode = this.node.ownerDocument.createElement('div'); this.tabContentNode.classList.add('tab-content'); + this.tabHeader = new PanelTabHeader(this.node); + this.node.appendChild(this.tabContentNode); parent.appendChild(this.node); } @@ -87,10 +89,16 @@ export default class PanelTabContainer { /** * Method to add a new PanelTab * @param tab New PanelTab instance + * @param onClick Optional function that is executed on the tab; Important: You must call `tabContainer.showTab()` yourself! */ - public addTab(tab: PanelTab, nav: PanelNavButton) { + public addTab(tab: PanelTab, onClick?: () => void) { this.tabs = [...this.tabs, tab]; - this.tabHeader.addNav(nav); + + const listener = (onClick) ? onClick : () => { + this.showTab(tab); + }; + + this.tabHeader.addNavButton(tab.getNavButton(listener)); this.tabContentNode.appendChild(tab.node); } @@ -115,6 +123,7 @@ export default class PanelTabContainer { public showCurrentTab() { this.currentTab.show(); } + /** * Hide currentTab */ From adedddd1e7eb0458abc7f7ddb605f9b42050892c Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Tue, 3 Mar 2020 10:53:25 +0100 Subject: [PATCH 40/63] hide unnecesary line using css datavisyn/tdp_core#285 --- src/styles/_view_lineup.scss | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/styles/_view_lineup.scss b/src/styles/_view_lineup.scss index b92610c60..9222ccfbf 100644 --- a/src/styles/_view_lineup.scss +++ b/src/styles/_view_lineup.scss @@ -248,7 +248,11 @@ $lu_assets: "~lineupjs/src/assets"; & > li { &:only-child() { - display: none; + border: 1px solid white; // overlay the parent element's border + width: 100%; + & > a { + display: none; + } } > a { From 4c3bed8eb3bc208f2dc10ad4466eaea01ac2443b Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Tue, 3 Mar 2020 11:06:38 +0100 Subject: [PATCH 41/63] Improved documentation of the IPanelTabExtensionDes interface datavisyn/tdp_core#285 --- src/extensions.ts | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/extensions.ts b/src/extensions.ts index b96726973..3387359c4 100644 --- a/src/extensions.ts +++ b/src/extensions.ts @@ -154,16 +154,29 @@ export interface IPanelTabExtension { export interface IPanelTabExtensionDesc extends IPluginDesc { /** - * @param CssClass css class for the button/header of the LineupSidePanel tab - * @param title title of the above button/header - * @param order position of tab `0 , 10, 20, ...` to order the tabs - * @param width width of the sidePanel - * @param shortcut add button in collapsed mode to access PanelTab @default false + * Css class for the PanelNavButton of the PanelTab */ cssClass: string; + + /** + * Title attribute PanelNavButton + */ title: string; + + /** + * Customize the PanelNavButtons' position (recommended to use multiples of 10) + */ order: number; + + /** + * Width of the PanelTab + */ width: string; + + /** + * If true a shortcut button is appended to the SidePanel header in collapsed mode + * @default false + */ shortcut?: boolean; load(): Promise; From 41332d93731866b9db2c54b04139d3ed2777059d Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Tue, 3 Mar 2020 11:10:16 +0100 Subject: [PATCH 42/63] Add spaces before comments datavisyn/tdp_core#285 --- src/lineup/internal/LineUpPanelActions.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/lineup/internal/LineUpPanelActions.ts b/src/lineup/internal/LineUpPanelActions.ts index 6a2c6cb60..809f1e63b 100644 --- a/src/lineup/internal/LineUpPanelActions.ts +++ b/src/lineup/internal/LineUpPanelActions.ts @@ -131,25 +131,25 @@ export default class LineUpPanelActions extends EventHandler { set collapse(value: boolean) { this.node.classList.toggle('collapsed', value); - //When this.options.enableSidePanel === 'top' `this.collapsed=true` gets called. Attempting to open the PanelTab produces an error. + // When this.options.enableSidePanel === 'top' `this.collapsed=true` gets called. Attempting to open the PanelTab produces an error. if (value && this.options.enableSidePanel !== 'top') { - this.tabContainer.hideCurrentTab(); //Hide the active PanelTab --> Inform its content to stop updating + this.tabContainer.hideCurrentTab(); // Hide the active PanelTab --> Inform its content to stop updating } else if (this.options.enableSidePanel !== 'top') { - this.tabContainer.showCurrentTab(); //Show the last active PanelTab --> Inform its content to start updating again + this.tabContainer.showCurrentTab(); // Show the last active PanelTab --> Inform its content to start updating again } } hide() { this.node.style.display = 'none'; - //Hide the active PanelTab and inform its content to stop updating + // Hide the active PanelTab and inform its content to stop updating this.tabContainer.hideCurrentTab(); } show() { this.node.style.display = 'flex'; - //Show the last active PanelTab and inform its content to start updating again + // Show the last active PanelTab and inform its content to start updating again this.tabContainer.showCurrentTab(); } @@ -462,10 +462,10 @@ export function findMappablePlugins(target: IDType, all: IPluginDesc[]) { if (idtype === target.id) { return true; } - //lookup the targets and check if our target is part of it + // lookup the targets and check if our target is part of it return resolve(idtype).getCanBeMappedTo().then((mappables: IDType[]) => mappables.some((d) => d.id === target.id)); } - //check which idTypes can be mapped to the target one + // check which idTypes can be mapped to the target one return Promise.all(idTypes.map(canBeMappedTo)).then((mappable: boolean[]) => { const valid = idTypes.filter((d, i) => mappable[i]); return all.filter((d) => valid.indexOf(d.idtype) >= 0); From e85d3a5c8891ce5a2bf798c11652bbda64f8457d Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Tue, 3 Mar 2020 12:13:03 +0100 Subject: [PATCH 43/63] Rename PanelDownloadButtonContainer to PanelDownloadButton datavisyn/tdp_core#285 --- src/lineup/internal/LineUpPanelActions.ts | 8 ++++---- ...lDownloadButtonContainer.ts => PanelDownloadButton.ts} | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) rename src/lineup/internal/panel/{PanelDownloadButtonContainer.ts => PanelDownloadButton.ts} (97%) diff --git a/src/lineup/internal/LineUpPanelActions.ts b/src/lineup/internal/LineUpPanelActions.ts index 809f1e63b..61b438db4 100644 --- a/src/lineup/internal/LineUpPanelActions.ts +++ b/src/lineup/internal/LineUpPanelActions.ts @@ -1,5 +1,5 @@ -import {SidePanel, spaceFillingRule, IGroupSearchItem, SearchBox, LocalDataProvider, createStackDesc, IColumnDesc, createScriptDesc, createSelectionDesc, createAggregateDesc, createGroupDesc, Ranking, createImpositionDesc, createNestedDesc, createReduceDesc, isSupportType, Column, IItem} from 'lineupjs'; +import {SidePanel, spaceFillingRule, IGroupSearchItem, LocalDataProvider, createStackDesc, IColumnDesc, createScriptDesc, createSelectionDesc, createAggregateDesc, createGroupDesc, Ranking, createImpositionDesc, createNestedDesc, createReduceDesc, isSupportType, Column, IItem} from 'lineupjs'; import {IDType, resolve} from 'phovea_core/src/idtype'; import {IPlugin, IPluginDesc, list as listPlugins} from 'phovea_core/src/plugin'; import {editDialog} from '../../storage'; @@ -10,7 +10,7 @@ import { import {EventHandler} from 'phovea_core/src/event'; import {IARankingViewOptions} from '../ARankingView'; import {lazyDialogModule} from '../../dialogs'; -import PanelButton, {IPanelButton, PanelNavButton} from './panel/PanelButton'; +import PanelButton from './panel/PanelButton'; import PanelTabContainer from './panel/PanelTabContainer'; import {PanelTab, SidePanelTab} from './panel/PanelTab'; import SearchBoxProvider from './panel/SearchBoxProvider'; @@ -18,7 +18,7 @@ import PanelHeader from './panel/PanelHeader'; import PanelRankingButton from './panel/PanelRankingButton'; import PanelAddColumnButton from './panel/PanelAddColumnButton'; import i18n from 'phovea_core/src/i18n'; -import PanelDownloadButtonContainer from './panel/PanelDownloadButtonContainer'; +import PanelDownloadButton from './panel/PanelDownloadButton'; export interface ISearchOption { text: string; @@ -192,7 +192,7 @@ export default class LineUpPanelActions extends EventHandler { } if (this.options.enableDownload) { - const downloadButtonContainer = new PanelDownloadButtonContainer(buttons, this.provider, this.isTopMode); + const downloadButtonContainer = new PanelDownloadButton(buttons, this.provider, this.isTopMode); this.header.addButton(downloadButtonContainer); } diff --git a/src/lineup/internal/panel/PanelDownloadButtonContainer.ts b/src/lineup/internal/panel/PanelDownloadButton.ts similarity index 97% rename from src/lineup/internal/panel/PanelDownloadButtonContainer.ts rename to src/lineup/internal/panel/PanelDownloadButton.ts index 33c520f09..93a7b74fc 100644 --- a/src/lineup/internal/panel/PanelDownloadButtonContainer.ts +++ b/src/lineup/internal/panel/PanelDownloadButton.ts @@ -7,7 +7,7 @@ import i18n from 'phovea_core/src/i18n'; /** * A button dropdown to download selected/all rows of the ranking */ -export default class PanelDownloadButtonContainer implements IPanelButton { +export default class PanelDownloadButton implements IPanelButton { readonly node: HTMLElement; constructor(parent: HTMLElement, private provider: LocalDataProvider, isTopMode:boolean) { From 75c309eccf4653d2f111b2a0f9916524c39239d1 Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Tue, 3 Mar 2020 12:31:17 +0100 Subject: [PATCH 44/63] Imporve commnets datavisyn/tdp_core#285 --- src/extensions.ts | 2 +- src/lineup/internal/panel/PanelAddColumnButton.ts | 4 ++-- src/lineup/internal/panel/PanelHeader.ts | 4 ++-- src/lineup/internal/panel/PanelRankingButton.ts | 4 ++-- src/lineup/internal/panel/PanelTab.ts | 5 ++--- 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/extensions.ts b/src/extensions.ts index 3387359c4..af9b7cfd1 100644 --- a/src/extensions.ts +++ b/src/extensions.ts @@ -154,7 +154,7 @@ export interface IPanelTabExtension { export interface IPanelTabExtensionDesc extends IPluginDesc { /** - * Css class for the PanelNavButton of the PanelTab + * CSS class for the PanelNavButton of the PanelTab */ cssClass: string; diff --git a/src/lineup/internal/panel/PanelAddColumnButton.ts b/src/lineup/internal/panel/PanelAddColumnButton.ts index 1b34758d9..fed8a938f 100644 --- a/src/lineup/internal/panel/PanelAddColumnButton.ts +++ b/src/lineup/internal/panel/PanelAddColumnButton.ts @@ -5,14 +5,14 @@ import i18n from 'phovea_core/src/i18n'; /** * Div HTMLElement that contains a button and a SearchBox. - * The SearchBox is by default hidden and can bit toggled by the button + * The SearchBox is hidden by default and can be toggled by the button. */ export default class PanelAddColumnButton implements IPanelButton { readonly node: HTMLElement; /** * * @param parent The parent HTML DOM element - * @param search LIneup SearchBox instance + * @param search LineUp SearchBox instance */ constructor(parent: HTMLElement, private readonly search: SearchBox) { this.node = parent.ownerDocument.createElement('div'); diff --git a/src/lineup/internal/panel/PanelHeader.ts b/src/lineup/internal/panel/PanelHeader.ts index 8e975a75d..f02a9f7b1 100644 --- a/src/lineup/internal/panel/PanelHeader.ts +++ b/src/lineup/internal/panel/PanelHeader.ts @@ -10,8 +10,8 @@ export default class PanelHeader { /** * - * @param parent The parent HTML DOM element - * @param isTopMode Is top mode + * @param parent The parent HTML DOM element. + * @param isTopMode Is the SidePanel collapsed or not. */ constructor(parent: HTMLElement) { this.node = parent.ownerDocument.createElement('header'); diff --git a/src/lineup/internal/panel/PanelRankingButton.ts b/src/lineup/internal/panel/PanelRankingButton.ts index be30eb9e2..12966f523 100644 --- a/src/lineup/internal/panel/PanelRankingButton.ts +++ b/src/lineup/internal/panel/PanelRankingButton.ts @@ -2,8 +2,8 @@ import {IPanelButton} from './PanelButton'; import {LocalDataProvider, Ranking} from 'lineupjs'; /** - * Plain HTML button with a custom title, CSS class and an onClick function - * Injects through the onClick callback the current ranking + * Plain HTML button with a custom title, CSS class and an onClick function. + * Injects through the onClick callback the current ranking. */ export default class PanelRankingButton implements IPanelButton { readonly node: HTMLElement; diff --git a/src/lineup/internal/panel/PanelTab.ts b/src/lineup/internal/panel/PanelTab.ts index 65374c137..1f3b57d83 100644 --- a/src/lineup/internal/panel/PanelTab.ts +++ b/src/lineup/internal/panel/PanelTab.ts @@ -14,7 +14,7 @@ export interface IPanelTabDesc { width: string; /** - * Css class for PanelNavButton of the PanelTab + * CSS class for PanelNavButton of the PanelTab */ cssClass: string; @@ -24,7 +24,7 @@ export interface IPanelTabDesc { title: string; /** - * Used to sort the PanelNavButtons + * Define the sort order of the PanelNavButtons */ order: number; @@ -58,7 +58,6 @@ export class PanelTab { private navButton: PanelNavButton; /** - * @param * @param parent The parent HTML DOM element * @param options Extra styles to apply to the PanelTab */ From 6d42673ddb41164e0e7aee2affa37bc7d0709d4c Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Tue, 3 Mar 2020 12:48:28 +0100 Subject: [PATCH 45/63] Fix markup for PanelNavButton datavisyn/tdp_core#285 --- src/lineup/internal/panel/PanelButton.ts | 2 +- src/lineup/internal/panel/PanelTab.ts | 6 +++--- src/lineup/internal/panel/PanelTabContainer.ts | 3 +-- src/styles/_view_lineup.scss | 8 ++++++++ 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/lineup/internal/panel/PanelButton.ts b/src/lineup/internal/panel/PanelButton.ts index 4901aeb42..90a1e5438 100644 --- a/src/lineup/internal/panel/PanelButton.ts +++ b/src/lineup/internal/panel/PanelButton.ts @@ -51,7 +51,7 @@ export class PanelNavButton implements IPanelButton { constructor(parent: HTMLElement, onClick: () => void, options: IPanelTabDesc) { this.node = parent.ownerDocument.createElement('li'); this.order = options.order; - this.node.insertAdjacentHTML('afterbegin', ` ${options.title || ''}`); + this.node.insertAdjacentHTML('afterbegin', `  ${options.title || ''}`); this.node.querySelector('a').addEventListener('click', (evt) => { evt.preventDefault(); onClick(); diff --git a/src/lineup/internal/panel/PanelTab.ts b/src/lineup/internal/panel/PanelTab.ts index 1f3b57d83..a837222f6 100644 --- a/src/lineup/internal/panel/PanelTab.ts +++ b/src/lineup/internal/panel/PanelTab.ts @@ -71,7 +71,7 @@ export class PanelTab { } /** - * Show self/ add active class + * Show this tab and fire the `PanelTabEvents.SHOW_PANEL` event. */ public show() { this.node.classList.add('active'); @@ -80,7 +80,7 @@ export class PanelTab { } /** - * Hide self/ remove active class + * Hide this tab and fire the `PanelTabEvents.HIDE_PANEL` event. */ public hide() { this.node.classList.remove('active'); @@ -115,7 +115,7 @@ export class SidePanelTab extends PanelTab { /** * @param parent The parent HTML DOM element * @param search LineUp SearchBox - * @param ctx Ctx + * @param ctx LineUp context * @param doc Document */ constructor(parent: HTMLElement, private readonly search: SearchBox, ctx: any, doc = document, options: IPanelTabDesc) { diff --git a/src/lineup/internal/panel/PanelTabContainer.ts b/src/lineup/internal/panel/PanelTabContainer.ts index 94b1c7aa9..e9b5dc098 100644 --- a/src/lineup/internal/panel/PanelTabContainer.ts +++ b/src/lineup/internal/panel/PanelTabContainer.ts @@ -21,7 +21,6 @@ class PanelTabHeader { } /** - * * @param navTabs NavTabs array sorted according to their order property * @param nav New nav being added to the NavTabs */ @@ -55,8 +54,8 @@ class PanelTabHeader { export default class PanelTabContainer { readonly node: HTMLElement; - private parent: HTMLElement; readonly tabContentNode: HTMLElement; + private parent: HTMLElement; private tabs: PanelTab[] = []; private tabHeader: PanelTabHeader; private currentTab: PanelTab; diff --git a/src/styles/_view_lineup.scss b/src/styles/_view_lineup.scss index 9222ccfbf..a6e28aaf9 100644 --- a/src/styles/_view_lineup.scss +++ b/src/styles/_view_lineup.scss @@ -250,6 +250,7 @@ $lu_assets: "~lineupjs/src/assets"; &:only-child() { border: 1px solid white; // overlay the parent element's border width: 100%; + & > a { display: none; } @@ -260,6 +261,13 @@ $lu_assets: "~lineupjs/src/assets"; padding: 5px 8.5px; cursor: pointer; text-align-last: center; + display: flex; + align-items: center; + text-align-last: auto; + + & > i { + align-self: flex-start; + } } } } From ad3540edaa01930f51b6c98fa38bcd031dec40e5 Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Tue, 3 Mar 2020 13:09:49 +0100 Subject: [PATCH 46/63] Add default configuration to PanelTab datavisyn/tdp_core#285 --- src/lineup/internal/LineUpPanelActions.ts | 10 +--------- src/lineup/internal/panel/PanelTab.ts | 16 ++++++++++------ 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/lineup/internal/LineUpPanelActions.ts b/src/lineup/internal/LineUpPanelActions.ts index 61b438db4..3c0801d69 100644 --- a/src/lineup/internal/LineUpPanelActions.ts +++ b/src/lineup/internal/LineUpPanelActions.ts @@ -90,16 +90,8 @@ export default class LineUpPanelActions extends EventHandler { this.node.classList.add('lu-side-panel-top'); } else { - const options = { - cssClass: 'fa fa-sliders', - title: 'Ranking Configuration', - width: '21em', - order: 0 - }; - - const sidePanel = new SidePanelTab(this.node, this.searchBoxProvider.createSearchBox(), ctx, doc, options); + const sidePanel = new SidePanelTab(this.node, this.searchBoxProvider.createSearchBox(), ctx, doc); this.panel = sidePanel.panel; - this.tabContainer = new PanelTabContainer(this.node); this.tabContainer.addTab(sidePanel); this.tabContainer.showTab(sidePanel); diff --git a/src/lineup/internal/panel/PanelTab.ts b/src/lineup/internal/panel/PanelTab.ts index a837222f6..8f1ef5771 100644 --- a/src/lineup/internal/panel/PanelTab.ts +++ b/src/lineup/internal/panel/PanelTab.ts @@ -2,6 +2,7 @@ import {SidePanel, SearchBox} from 'lineupjs'; import {ISearchOption} from '../LineUpPanelActions'; import {EventHandler} from 'phovea_core/src/event'; import PanelButton, {PanelNavButton} from './PanelButton'; +import {mixin} from 'phovea_core/src/index'; /** * Interface for the options parameter of PanelTab @@ -53,21 +54,24 @@ export class PanelTab { readonly node: HTMLElement; readonly events: PanelTabEvents; - readonly width: string; - + readonly options: IPanelTabDesc = { + cssClass: 'fa fa-sliders', + title: 'Ranking Configuration', + width: '21em', + order: 0 + } private navButton: PanelNavButton; /** * @param parent The parent HTML DOM element * @param options Extra styles to apply to the PanelTab */ - constructor(parent: HTMLElement, private options: IPanelTabDesc) { + constructor(parent: HTMLElement, options?: IPanelTabDesc) { this.events = new PanelTabEvents(); this.node = parent.ownerDocument.createElement('div'); this.node.classList.add('tab-pane'); this.node.setAttribute('role', 'tabpanel'); - const o = Object.assign({}, options); - this.width = o.width; + mixin(this.options, options) } /** @@ -118,7 +122,7 @@ export class SidePanelTab extends PanelTab { * @param ctx LineUp context * @param doc Document */ - constructor(parent: HTMLElement, private readonly search: SearchBox, ctx: any, doc = document, options: IPanelTabDesc) { + constructor(parent: HTMLElement, private readonly search: SearchBox, ctx: any, doc = document, options?: IPanelTabDesc) { super(parent, options); this.node.classList.add('default'); this.panel = new SidePanel(ctx, doc, { From 17d83f7b83cf6d07acf692b16835ea474d86481e Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Tue, 3 Mar 2020 13:10:15 +0100 Subject: [PATCH 47/63] Minor fix datavisyn/tdp_core#285 --- src/lineup/internal/panel/PanelTabContainer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lineup/internal/panel/PanelTabContainer.ts b/src/lineup/internal/panel/PanelTabContainer.ts index e9b5dc098..9bd88ac17 100644 --- a/src/lineup/internal/panel/PanelTabContainer.ts +++ b/src/lineup/internal/panel/PanelTabContainer.ts @@ -110,7 +110,7 @@ export default class PanelTabContainer { this.currentTab.hide(); } - this.resizeNode(tab.width); + this.resizeNode(tab.options.width); tab.show(); this.currentTab = tab; } From 17afe9184608e5d3c2e4f1f26c242a0ca7110747 Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Tue, 3 Mar 2020 13:20:08 +0100 Subject: [PATCH 48/63] Fix linter error datavisyn/tdp_core#285 --- src/lineup/internal/panel/PanelTab.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lineup/internal/panel/PanelTab.ts b/src/lineup/internal/panel/PanelTab.ts index 8f1ef5771..c3514991c 100644 --- a/src/lineup/internal/panel/PanelTab.ts +++ b/src/lineup/internal/panel/PanelTab.ts @@ -59,7 +59,7 @@ export class PanelTab { title: 'Ranking Configuration', width: '21em', order: 0 - } + }; private navButton: PanelNavButton; /** @@ -71,7 +71,7 @@ export class PanelTab { this.node = parent.ownerDocument.createElement('div'); this.node.classList.add('tab-pane'); this.node.setAttribute('role', 'tabpanel'); - mixin(this.options, options) + mixin(this.options, options); } /** From 1a91a8ae4445454da61593d73ce0cdfc7b3fa3fc Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Tue, 3 Mar 2020 13:46:00 +0100 Subject: [PATCH 49/63] Improve comments datavisyn/tdp_core#285 --- .../internal/panel/PanelTabContainer.ts | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/lineup/internal/panel/PanelTabContainer.ts b/src/lineup/internal/panel/PanelTabContainer.ts index 9bd88ac17..d71b7b6cd 100644 --- a/src/lineup/internal/panel/PanelTabContainer.ts +++ b/src/lineup/internal/panel/PanelTabContainer.ts @@ -49,7 +49,7 @@ class PanelTabHeader { /** - * The PanelTabContainer creates tab able nav buttons that toggle their corresponding PanelTab + * The PanelTabContainer creates tab able nav buttons that toggle their corresponding PanelTab. */ export default class PanelTabContainer { @@ -62,7 +62,7 @@ export default class PanelTabContainer { /** - * @param parent The parent HTML DOM element + * @param parent The parent HTML DOM element. */ constructor(parent: HTMLElement) { this.parent = parent; @@ -78,17 +78,17 @@ export default class PanelTabContainer { } /** - * Resize the Panel to fit the content of the new tab - * @param width width the PanelTabContainer should have; + * Resize the Panel to fit the content of the new tab. + * @param width width the PanelTabContainer should have. */ resizeNode(width: string) { this.parent.style.width = width; } /** - * Method to add a new PanelTab - * @param tab New PanelTab instance - * @param onClick Optional function that is executed on the tab; Important: You must call `tabContainer.showTab()` yourself! + * Method to add a new PanelTab. + * @param tab New PanelTab instance. + * @param onClick Optional function that is executed on the tab; Important: You must call `tabContainer.showTab()` yourself!. */ public addTab(tab: PanelTab, onClick?: () => void) { this.tabs = [...this.tabs, tab]; @@ -102,8 +102,8 @@ export default class PanelTabContainer { } /** - * Close currentTab and show new PanelTab - * @param tab A PanelTab instance + * Close currentTab and show new PanelTab. + * @param tab A PanelTab instance. */ public showTab(tab: PanelTab) { if (this.currentTab) { @@ -116,15 +116,15 @@ export default class PanelTabContainer { } /** - * Show last opened PanelTab - * Used when the LineUpPanelActions reopens to show the last open PanelTab + * Show last opened PanelTab. + * Used when the LineUpPanelActions reopens to show the last open PanelTab. */ public showCurrentTab() { this.currentTab.show(); } /** - * Hide currentTab + * Hide currentTab. */ public hideCurrentTab() { this.currentTab.hide(); From 520a206ac3dc0a31ec3e9319cdf4d9f02a82590d Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Tue, 3 Mar 2020 13:50:14 +0100 Subject: [PATCH 50/63] Sort plugins to be appended in the correct order datavisyn/tdp_core#285 --- src/lineup/internal/LineUpPanelActions.ts | 2 +- .../internal/panel/PanelTabContainer.ts | 24 +++---------------- 2 files changed, 4 insertions(+), 22 deletions(-) diff --git a/src/lineup/internal/LineUpPanelActions.ts b/src/lineup/internal/LineUpPanelActions.ts index 3c0801d69..6cace1f72 100644 --- a/src/lineup/internal/LineUpPanelActions.ts +++ b/src/lineup/internal/LineUpPanelActions.ts @@ -233,7 +233,7 @@ export default class LineUpPanelActions extends EventHandler { } private appendExtraTabs() { - const plugins = listPlugins(EP_TDP_CORE_LINEUP_PANEL_TAB); + const plugins = listPlugins(EP_TDP_CORE_LINEUP_PANEL_TAB).sort((a, b) => a.order - b.order); plugins.forEach((plugin) => { let isLoaded = false; const tab = new PanelTab(this.tabContainer.node, plugin); diff --git a/src/lineup/internal/panel/PanelTabContainer.ts b/src/lineup/internal/panel/PanelTabContainer.ts index d71b7b6cd..009795739 100644 --- a/src/lineup/internal/panel/PanelTabContainer.ts +++ b/src/lineup/internal/panel/PanelTabContainer.ts @@ -4,8 +4,8 @@ import {PanelNavButton} from './PanelButton'; /** - * Acts as the navigation to the TabPanels - * Allows the addition in the correct order of PanelNavButtons + * The header of the PanelTab + * Contains the PanelNavButtons that toggle the PanelTab */ class PanelTabHeader { public node: HTMLElement; @@ -21,29 +21,11 @@ class PanelTabHeader { } /** - * @param navTabs NavTabs array sorted according to their order property - * @param nav New nav being added to the NavTabs - */ - getNextSibling(sortedNavTabs: PanelNavButton[], nav: PanelNavButton): PanelNavButton | null { - const navTabsLength = sortedNavTabs.length; - const navIndex = sortedNavTabs.findIndex((n) => n.order === nav.order); - return navIndex === navTabsLength - 1 ? null : sortedNavTabs[navIndex + 1]; - } - - /** - * Add nav-tab to the nav-tabs in the correct order. + * Append PanelNavButtons to PanelTabHeader * @param button PanelNavButton instance to add */ addNavButton(button: PanelNavButton) { - const sameOrderNav = this.navButtons.find((n) => n.order === button.order); // find nav with same order - this.navButtons = [...this.navButtons, button].sort((nav1, nav2) => nav1.order - nav2.order); this.node.appendChild(button.node); - const nextSibling = sameOrderNav || this.getNextSibling(this.navButtons, button); // if same order property append new nav before it - if (nextSibling) { - this.node.insertBefore(button.node, nextSibling.node); - } else { - this.node.appendChild(button.node); - } } } From 838ddf9ea65166afbec6c02205bde15a47ce948c Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Tue, 3 Mar 2020 14:03:04 +0100 Subject: [PATCH 51/63] Formatting datavisyn/tdp_core#285 --- src/extensions.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/extensions.ts b/src/extensions.ts index af9b7cfd1..64b60c272 100644 --- a/src/extensions.ts +++ b/src/extensions.ts @@ -9,7 +9,6 @@ import {IDType} from 'phovea_core/src/idtype'; import {IColumnDesc, Column, LocalDataProvider} from 'lineupjs'; import {EViewMode} from './views/interfaces'; import {AppHeader} from 'phovea_ui/src/header'; -import {IPanelTabDesc} from './lineup/internal/panel/PanelTab'; export * from './tour/extensions'; @@ -248,14 +247,14 @@ export interface IView extends IEventHandler { /** * optional natural size used when stacking the view on top of each other */ - readonly naturalSize?: [number, number] | 'auto'; + readonly naturalSize?: [number, number]|'auto'; /** * initialized this view * @param {HTMLElement} params place to put parameter forms * @param {(name: string, value: any, previousValue: any) => Promise} onParameterChange instead of directly setting the parameter this method should be used to track the changes */ - init(params: HTMLElement, onParameterChange: (name: string, value: any, previousValue: any) => PromiseLike): PromiseLike | undefined; + init(params: HTMLElement, onParameterChange: (name: string, value: any, previousValue: any) => PromiseLike): PromiseLike|undefined; /** * changes the input selection as given to the constructor of this class @@ -347,7 +346,7 @@ export interface IViewPluginDesc extends IPluginDesc { /** * optional security check to show only certain views */ - security?: string | ((user: IUser) => boolean); + security?: string|((user: IUser) => boolean); /** * a lot of topics/tags describing this view From 779b4eb8ab63bcccd66b9ef510e47d450f3ad30f Mon Sep 17 00:00:00 2001 From: Holger Stitz Date: Wed, 4 Mar 2020 13:06:44 +0100 Subject: [PATCH 52/63] Replace mixin() with Object.assign() Prefer native JS functions if they offer the same functionality. --- src/lineup/internal/panel/PanelTab.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lineup/internal/panel/PanelTab.ts b/src/lineup/internal/panel/PanelTab.ts index c3514991c..652235e69 100644 --- a/src/lineup/internal/panel/PanelTab.ts +++ b/src/lineup/internal/panel/PanelTab.ts @@ -2,7 +2,6 @@ import {SidePanel, SearchBox} from 'lineupjs'; import {ISearchOption} from '../LineUpPanelActions'; import {EventHandler} from 'phovea_core/src/event'; import PanelButton, {PanelNavButton} from './PanelButton'; -import {mixin} from 'phovea_core/src/index'; /** * Interface for the options parameter of PanelTab @@ -71,7 +70,7 @@ export class PanelTab { this.node = parent.ownerDocument.createElement('div'); this.node.classList.add('tab-pane'); this.node.setAttribute('role', 'tabpanel'); - mixin(this.options, options); + Object.assign(this.options, options); } /** From c5aab9241746b104b587526248b4dd4b00a4acfc Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Thu, 12 Mar 2020 17:02:27 +0100 Subject: [PATCH 53/63] refactor insertAdjacentHTML() to innerHTML() datavisyn/tdp_core#257 --- src/lineup/internal/panel/PanelButton.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lineup/internal/panel/PanelButton.ts b/src/lineup/internal/panel/PanelButton.ts index 90a1e5438..1a9223259 100644 --- a/src/lineup/internal/panel/PanelButton.ts +++ b/src/lineup/internal/panel/PanelButton.ts @@ -51,7 +51,7 @@ export class PanelNavButton implements IPanelButton { constructor(parent: HTMLElement, onClick: () => void, options: IPanelTabDesc) { this.node = parent.ownerDocument.createElement('li'); this.order = options.order; - this.node.insertAdjacentHTML('afterbegin', `  ${options.title || ''}`); + this.node.innerHTML = `  ${options.title || ''}`; this.node.querySelector('a').addEventListener('click', (evt) => { evt.preventDefault(); onClick(); From 4d7ddaa8e11fa9e16f530bfeeb40b5cc8f76107f Mon Sep 17 00:00:00 2001 From: oltionchampari Date: Thu, 12 Mar 2020 17:27:12 +0100 Subject: [PATCH 54/63] Fix IPanelTabExtension inteface datavisyn/tdp_core#257 --- src/extensions.ts | 3 ++- src/lineup/internal/LineUpPanelActions.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/extensions.ts b/src/extensions.ts index 44f5eef09..abde95c8a 100644 --- a/src/extensions.ts +++ b/src/extensions.ts @@ -9,6 +9,7 @@ import {IDType} from 'phovea_core/src/idtype'; import {IColumnDesc, Column, LocalDataProvider} from 'lineupjs'; import {EViewMode} from './views/interfaces'; import {AppHeader} from 'phovea_ui/src/header'; +import {PanelTabEvents} from './lineup/internal/panel/PanelTab'; export * from './tour/extensions'; @@ -148,7 +149,7 @@ export interface IScoreColumnPatcherExtensionDesc extends IPluginDesc { export interface IPanelTabExtension { desc: IPanelTabExtensionDesc; - factory(parent: HTMLElement, provider: LocalDataProvider, desc: IRankingButtonExtensionDesc, idType: IDType, extraArgs: object): Promise; + factory(parent: HTMLElement, provider: LocalDataProvider, desc: IRankingButtonExtensionDesc, events: PanelTabEvents, extraArgs?: object): Promise; } export interface IPanelTabExtensionDesc extends IPluginDesc { diff --git a/src/lineup/internal/LineUpPanelActions.ts b/src/lineup/internal/LineUpPanelActions.ts index 6cace1f72..e7a10361e 100644 --- a/src/lineup/internal/LineUpPanelActions.ts +++ b/src/lineup/internal/LineUpPanelActions.ts @@ -19,6 +19,7 @@ import PanelRankingButton from './panel/PanelRankingButton'; import PanelAddColumnButton from './panel/PanelAddColumnButton'; import i18n from 'phovea_core/src/i18n'; import PanelDownloadButton from './panel/PanelDownloadButton'; +import {IPanelTabExtension} from '../../extensions'; export interface ISearchOption { text: string; @@ -246,7 +247,7 @@ export default class LineUpPanelActions extends EventHandler { this.tabContainer.showTab(tab); } else { - plugin.load().then((p) => { + plugin.load().then((p: IPanelTabExtension) => { p.factory(tab.node, this.provider, p.desc, tab.events); this.collapse = false; // expand side panel this.tabContainer.showTab(tab); From 0e06bb548d320af5a227009593113cab9492c2f5 Mon Sep 17 00:00:00 2001 From: Holger Stitz Date: Fri, 13 Mar 2020 14:05:15 +0100 Subject: [PATCH 55/63] Add typings for `ctx` variable --- src/lineup/internal/LineUpPanelActions.ts | 4 ++-- src/lineup/internal/panel/PanelTab.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lineup/internal/LineUpPanelActions.ts b/src/lineup/internal/LineUpPanelActions.ts index e7a10361e..914d64a52 100644 --- a/src/lineup/internal/LineUpPanelActions.ts +++ b/src/lineup/internal/LineUpPanelActions.ts @@ -1,5 +1,5 @@ -import {SidePanel, spaceFillingRule, IGroupSearchItem, LocalDataProvider, createStackDesc, IColumnDesc, createScriptDesc, createSelectionDesc, createAggregateDesc, createGroupDesc, Ranking, createImpositionDesc, createNestedDesc, createReduceDesc, isSupportType, Column, IItem} from 'lineupjs'; +import {SidePanel, spaceFillingRule, IGroupSearchItem, LocalDataProvider, createStackDesc, IColumnDesc, createScriptDesc, createSelectionDesc, createAggregateDesc, createGroupDesc, Ranking, createImpositionDesc, createNestedDesc, createReduceDesc, IEngineRankingContext, IRenderContext, IRankingHeaderContextContainer} from 'lineupjs'; import {IDType, resolve} from 'phovea_core/src/idtype'; import {IPlugin, IPluginDesc, list as listPlugins} from 'phovea_core/src/plugin'; import {editDialog} from '../../storage'; @@ -78,7 +78,7 @@ export default class LineUpPanelActions extends EventHandler { private overview: HTMLElement; private wasCollapsed = false; - constructor(protected readonly provider: LocalDataProvider, ctx: any, private readonly options: Readonly, doc = document) { + constructor(protected readonly provider: LocalDataProvider, ctx: IRankingHeaderContextContainer & IRenderContext & IEngineRankingContext, private readonly options: Readonly, doc = document) { super(); this.node = doc.createElement('aside'); this.node.classList.add('lu-side-panel-wrapper'); diff --git a/src/lineup/internal/panel/PanelTab.ts b/src/lineup/internal/panel/PanelTab.ts index 652235e69..b3f08ec95 100644 --- a/src/lineup/internal/panel/PanelTab.ts +++ b/src/lineup/internal/panel/PanelTab.ts @@ -1,4 +1,4 @@ -import {SidePanel, SearchBox} from 'lineupjs'; +import {SidePanel, SearchBox, IEngineRankingContext, IRenderContext, IRankingHeaderContextContainer} from 'lineupjs'; import {ISearchOption} from '../LineUpPanelActions'; import {EventHandler} from 'phovea_core/src/event'; import PanelButton, {PanelNavButton} from './PanelButton'; @@ -121,7 +121,7 @@ export class SidePanelTab extends PanelTab { * @param ctx LineUp context * @param doc Document */ - constructor(parent: HTMLElement, private readonly search: SearchBox, ctx: any, doc = document, options?: IPanelTabDesc) { + constructor(parent: HTMLElement, private readonly search: SearchBox, ctx: IRankingHeaderContextContainer & IRenderContext & IEngineRankingContext, doc = document, options?: IPanelTabDesc) { super(parent, options); this.node.classList.add('default'); this.panel = new SidePanel(ctx, doc, { From a50110081556a6319d8cd1bd3d641453c34baa0f Mon Sep 17 00:00:00 2001 From: Holger Stitz Date: Fri, 13 Mar 2020 14:05:44 +0100 Subject: [PATCH 56/63] Use `this.parent` instead of `document.body` --- src/lineup/internal/panel/PanelTab.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/lineup/internal/panel/PanelTab.ts b/src/lineup/internal/panel/PanelTab.ts index b3f08ec95..a18e4e5f3 100644 --- a/src/lineup/internal/panel/PanelTab.ts +++ b/src/lineup/internal/panel/PanelTab.ts @@ -65,7 +65,7 @@ export class PanelTab { * @param parent The parent HTML DOM element * @param options Extra styles to apply to the PanelTab */ - constructor(parent: HTMLElement, options?: IPanelTabDesc) { + constructor(private parent: HTMLElement, options?: IPanelTabDesc) { this.events = new PanelTabEvents(); this.node = parent.ownerDocument.createElement('div'); this.node.classList.add('tab-pane'); @@ -92,8 +92,7 @@ export class PanelTab { } getNavButton(listener): PanelNavButton { - // Note: `document.body` is used only for `parent.ownerDocument.createElement()` inside the button - this.navButton = new PanelNavButton(document.body, listener, this.options); + this.navButton = new PanelNavButton(this.parent, listener, this.options); return this.navButton; } @@ -102,8 +101,7 @@ export class PanelTab { this.navButton.click(); }; - // Note: `document.body` is used only for `parent.ownerDocument.createElement()` inside the button - return new PanelButton(document.body, this.options.title, 'fa ' + this.options.cssClass + ' shortcut-nav', onClick); + return new PanelButton(this.parent, this.options.title, 'fa ' + this.options.cssClass + ' shortcut-nav', onClick); } } From 7609faa5538f6680edf75890ddd9e24e3d905b97 Mon Sep 17 00:00:00 2001 From: Holger Stitz Date: Fri, 13 Mar 2020 14:13:01 +0100 Subject: [PATCH 57/63] Remove unused variable --- src/lineup/internal/panel/PanelTabContainer.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lineup/internal/panel/PanelTabContainer.ts b/src/lineup/internal/panel/PanelTabContainer.ts index 009795739..dcda907f5 100644 --- a/src/lineup/internal/panel/PanelTabContainer.ts +++ b/src/lineup/internal/panel/PanelTabContainer.ts @@ -9,7 +9,6 @@ import {PanelNavButton} from './PanelButton'; */ class PanelTabHeader { public node: HTMLElement; - public navButtons: PanelNavButton[] = []; /** * @param parent The parent HTML DOM element From e8a6ae3b5bffdc6ee4f2aae5535f9687775d49a7 Mon Sep 17 00:00:00 2001 From: Holger Stitz Date: Fri, 13 Mar 2020 14:15:03 +0100 Subject: [PATCH 58/63] Initialize search box options with empty object --- src/lineup/internal/panel/SearchBoxProvider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lineup/internal/panel/SearchBoxProvider.ts b/src/lineup/internal/panel/SearchBoxProvider.ts index d29481b55..c66f75cac 100644 --- a/src/lineup/internal/panel/SearchBoxProvider.ts +++ b/src/lineup/internal/panel/SearchBoxProvider.ts @@ -21,7 +21,7 @@ export default class SearchBoxProvider { * Create a new LineUp SearchBox. The instance is added to the internal list and returned. * @returns A new LineUp SearchBox instance */ - createSearchBox(options?: Partial>): SearchBox { + createSearchBox(options: Partial> = {}): SearchBox { const mergedOptions = Object.assign({ placeholder: i18n.t('tdp:core.lineup.LineupPanelActions.searchPlaceholder') }, options); From 0141585b05f59ed5e5dec70d5b1f3bb57f031902 Mon Sep 17 00:00:00 2001 From: Holger Stitz Date: Fri, 13 Mar 2020 14:53:27 +0100 Subject: [PATCH 59/63] Add NullTabContainer + refactor PanelTabContainer --- src/lineup/internal/LineUpPanelActions.ts | 16 +-- .../internal/panel/PanelTabContainer.ts | 100 ++++++++++++++++-- 2 files changed, 101 insertions(+), 15 deletions(-) diff --git a/src/lineup/internal/LineUpPanelActions.ts b/src/lineup/internal/LineUpPanelActions.ts index 914d64a52..d7394386c 100644 --- a/src/lineup/internal/LineUpPanelActions.ts +++ b/src/lineup/internal/LineUpPanelActions.ts @@ -11,7 +11,7 @@ import {EventHandler} from 'phovea_core/src/event'; import {IARankingViewOptions} from '../ARankingView'; import {lazyDialogModule} from '../../dialogs'; import PanelButton from './panel/PanelButton'; -import PanelTabContainer from './panel/PanelTabContainer'; +import {ITabContainer, PanelTabContainer, NullTabContainer} from './panel/PanelTabContainer'; import {PanelTab, SidePanelTab} from './panel/PanelTab'; import SearchBoxProvider from './panel/SearchBoxProvider'; import PanelHeader from './panel/PanelHeader'; @@ -73,7 +73,7 @@ export default class LineUpPanelActions extends EventHandler { readonly node: HTMLElement; // wrapper node private readonly header: PanelHeader; - private readonly tabContainer: PanelTabContainer; + private readonly tabContainer: ITabContainer; private overview: HTMLElement; private wasCollapsed = false; @@ -89,8 +89,9 @@ export default class LineUpPanelActions extends EventHandler { if (this.options.enableSidePanel === 'top') { this.node.classList.add('lu-side-panel-top'); - } else { + this.tabContainer = new NullTabContainer(); // tab container without functionality + } else { const sidePanel = new SidePanelTab(this.node, this.searchBoxProvider.createSearchBox(), ctx, doc); this.panel = sidePanel.panel; this.tabContainer = new PanelTabContainer(this.node); @@ -124,11 +125,10 @@ export default class LineUpPanelActions extends EventHandler { set collapse(value: boolean) { this.node.classList.toggle('collapsed', value); - // When this.options.enableSidePanel === 'top' `this.collapsed=true` gets called. Attempting to open the PanelTab produces an error. - if (value && this.options.enableSidePanel !== 'top') { - this.tabContainer.hideCurrentTab(); // Hide the active PanelTab --> Inform its content to stop updating - } else if (this.options.enableSidePanel !== 'top') { - this.tabContainer.showCurrentTab(); // Show the last active PanelTab --> Inform its content to start updating again + if(value) { + this.tabContainer.hideCurrentTab(); // Hide the active PanelTab and inform its content to stop updating + } else { + this.tabContainer.showCurrentTab(); // Show the last active PanelTab and inform its content to start updating again } } diff --git a/src/lineup/internal/panel/PanelTabContainer.ts b/src/lineup/internal/panel/PanelTabContainer.ts index dcda907f5..49babeee9 100644 --- a/src/lineup/internal/panel/PanelTabContainer.ts +++ b/src/lineup/internal/panel/PanelTabContainer.ts @@ -28,14 +28,100 @@ class PanelTabHeader { } } +export interface ITabContainer { + + /** + * HTMLElement of the tab container + */ + readonly node: HTMLElement; + + /** + * Resize the Panel to fit the content of the new tab. + * @param width width the PanelTabContainer should have. + */ + resizeNode(width: string): void; + + /** + * Method to add a new PanelTab. + * @param tab New PanelTab instance. + * @param onClick Optional function that is executed on the tab; Important: You must call `tabContainer.showTab()` yourself!. + */ + addTab(tab: PanelTab, onClick?: () => void): void; + + /** + * Close currentTab and show new PanelTab. + * @param tab A PanelTab instance. + */ + showTab(tab: PanelTab): void; + + /** + * Show last opened PanelTab. + * Used when the LineUpPanelActions reopens to show the last open PanelTab. + */ + showCurrentTab(): void; + + /** + * Hide currentTab. + */ + hideCurrentTab(): void; +} + +/** + * The NullTabContainer does not have any functionality. + * The public functions have no operation and the public properties are dummy HTMLElements. + */ +export class NullTabContainer implements ITabContainer { + readonly node: HTMLElement = null; + + /** + * Resize the Panel to fit the content of the new tab. + * @param width width the PanelTabContainer should have. + */ + resizeNode(width: string): void { + // noop + } + + /** + * Method to add a new PanelTab. + * @param tab New PanelTab instance. + * @param onClick Optional function that is executed on the tab; Important: You must call `tabContainer.showTab()` yourself!. + */ + addTab(tab: PanelTab, onClick?: () => void): void { + // noop + } + + /** + * Close currentTab and show new PanelTab. + * @param tab A PanelTab instance. + */ + showTab(tab: PanelTab): void { + // noop + } + + /** + * Show last opened PanelTab. + * Used when the LineUpPanelActions reopens to show the last open PanelTab. + */ + showCurrentTab(): void { + // noop + } + + /** + * Hide currentTab. + */ + hideCurrentTab(): void { + // noop + } +} /** * The PanelTabContainer creates tab able nav buttons that toggle their corresponding PanelTab. */ -export default class PanelTabContainer { +export class PanelTabContainer implements ITabContainer { readonly node: HTMLElement; - readonly tabContentNode: HTMLElement; + + private readonly tabContentNode: HTMLElement; private parent: HTMLElement; private tabs: PanelTab[] = []; private tabHeader: PanelTabHeader; @@ -62,7 +148,7 @@ export default class PanelTabContainer { * Resize the Panel to fit the content of the new tab. * @param width width the PanelTabContainer should have. */ - resizeNode(width: string) { + resizeNode(width: string): void { this.parent.style.width = width; } @@ -71,7 +157,7 @@ export default class PanelTabContainer { * @param tab New PanelTab instance. * @param onClick Optional function that is executed on the tab; Important: You must call `tabContainer.showTab()` yourself!. */ - public addTab(tab: PanelTab, onClick?: () => void) { + addTab(tab: PanelTab, onClick?: () => void): void { this.tabs = [...this.tabs, tab]; const listener = (onClick) ? onClick : () => { @@ -86,7 +172,7 @@ export default class PanelTabContainer { * Close currentTab and show new PanelTab. * @param tab A PanelTab instance. */ - public showTab(tab: PanelTab) { + showTab(tab: PanelTab): void { if (this.currentTab) { this.currentTab.hide(); } @@ -100,14 +186,14 @@ export default class PanelTabContainer { * Show last opened PanelTab. * Used when the LineUpPanelActions reopens to show the last open PanelTab. */ - public showCurrentTab() { + showCurrentTab(): void { this.currentTab.show(); } /** * Hide currentTab. */ - public hideCurrentTab() { + hideCurrentTab(): void { this.currentTab.hide(); } } From e5d204e6746c707e3657d478bd2cee6e12fb1524 Mon Sep 17 00:00:00 2001 From: Holger Stitz Date: Fri, 13 Mar 2020 15:35:18 +0100 Subject: [PATCH 60/63] Refactor PanelTab EP Remove PanelTabEvents and pass the PanelTab directly to the extension point to match other framework implementation. --- src/extensions.ts | 16 ++++++++++------ src/lineup/internal/LineUpPanelActions.ts | 2 +- src/lineup/internal/panel/PanelTab.ts | 23 ++++++++--------------- 3 files changed, 19 insertions(+), 22 deletions(-) diff --git a/src/extensions.ts b/src/extensions.ts index abde95c8a..27f4b2198 100644 --- a/src/extensions.ts +++ b/src/extensions.ts @@ -9,7 +9,7 @@ import {IDType} from 'phovea_core/src/idtype'; import {IColumnDesc, Column, LocalDataProvider} from 'lineupjs'; import {EViewMode} from './views/interfaces'; import {AppHeader} from 'phovea_ui/src/header'; -import {PanelTabEvents} from './lineup/internal/panel/PanelTab'; +import {PanelTab} from './lineup/internal/panel/PanelTab'; export * from './tour/extensions'; @@ -27,10 +27,6 @@ export const EXTENSION_POINT_TDP_VIEW_GROUPS = 'tdpViewGroups'; /** * Register a new tab to the LineupSidePanel. * Consists of a button/header to open the tab content and the tab content itself - * @factoryParam {HTMLElement} parent The node of the tab content created through the extension point - * @factoryParam {LocalDataProvider} provider The data of the current ranking - * @factoryParam {IPanelTabExtensionDesc} desc The phovea extension point description - * @factoryParam {PanelTabEvents} events Listen when the tab closes or opens */ export const EP_TDP_CORE_LINEUP_PANEL_TAB = 'epTdpCoreLineupPanelTab'; @@ -149,7 +145,14 @@ export interface IScoreColumnPatcherExtensionDesc extends IPluginDesc { export interface IPanelTabExtension { desc: IPanelTabExtensionDesc; - factory(parent: HTMLElement, provider: LocalDataProvider, desc: IRankingButtonExtensionDesc, events: PanelTabEvents, extraArgs?: object): Promise; + + /** + * Create and attach a new LineUp side panel + * @param tab PanelTab instance to attach the HTMLElement and listen to events + * @param provider The data of the current ranking + * @param desc The phovea extension point description + */ + factory(desc: IPanelTabExtensionDesc, tab: PanelTab, provider: LocalDataProvider): void; } export interface IPanelTabExtensionDesc extends IPluginDesc { @@ -181,6 +184,7 @@ export interface IPanelTabExtensionDesc extends IPluginDesc { load(): Promise; } + export interface IRankingButtonExtension { desc: IRankingButtonExtensionDesc; factory(desc: IRankingButtonExtensionDesc, idType: IDType, extraArgs: object): Promise; diff --git a/src/lineup/internal/LineUpPanelActions.ts b/src/lineup/internal/LineUpPanelActions.ts index d7394386c..fd9aa78a6 100644 --- a/src/lineup/internal/LineUpPanelActions.ts +++ b/src/lineup/internal/LineUpPanelActions.ts @@ -248,7 +248,7 @@ export default class LineUpPanelActions extends EventHandler { } else { plugin.load().then((p: IPanelTabExtension) => { - p.factory(tab.node, this.provider, p.desc, tab.events); + p.factory(p.desc, tab, this.provider); this.collapse = false; // expand side panel this.tabContainer.showTab(tab); isLoaded = true; diff --git a/src/lineup/internal/panel/PanelTab.ts b/src/lineup/internal/panel/PanelTab.ts index a18e4e5f3..56be8d053 100644 --- a/src/lineup/internal/panel/PanelTab.ts +++ b/src/lineup/internal/panel/PanelTab.ts @@ -36,23 +36,15 @@ export interface IPanelTabDesc { } /** - * Events for PanelTab for when its showed/hidden + * The PanelTab creates a tab component that with can be toggled through the PanelNavButton */ -export class PanelTabEvents extends EventHandler { +export class PanelTab extends EventHandler { static readonly SHOW_PANEL = 'showPanel'; static readonly HIDE_PANEL = 'hidePanel'; -} - -/** - * The PanelTab creates a tab component that with can be toggled through the PanelNavButton - */ -export class PanelTab { - readonly node: HTMLElement; - readonly events: PanelTabEvents; readonly options: IPanelTabDesc = { cssClass: 'fa fa-sliders', title: 'Ranking Configuration', @@ -66,7 +58,8 @@ export class PanelTab { * @param options Extra styles to apply to the PanelTab */ constructor(private parent: HTMLElement, options?: IPanelTabDesc) { - this.events = new PanelTabEvents(); + super(); + this.node = parent.ownerDocument.createElement('div'); this.node.classList.add('tab-pane'); this.node.setAttribute('role', 'tabpanel'); @@ -74,21 +67,21 @@ export class PanelTab { } /** - * Show this tab and fire the `PanelTabEvents.SHOW_PANEL` event. + * Show this tab and fire the `PanelTab.SHOW_PANEL` event. */ public show() { this.node.classList.add('active'); this.navButton.setActive(true); - this.events.fire(PanelTabEvents.SHOW_PANEL); + this.fire(PanelTab.SHOW_PANEL); } /** - * Hide this tab and fire the `PanelTabEvents.HIDE_PANEL` event. + * Hide this tab and fire the `PanelTab.HIDE_PANEL` event. */ public hide() { this.node.classList.remove('active'); this.navButton.setActive(false); - this.events.fire(PanelTabEvents.HIDE_PANEL); + this.fire(PanelTab.HIDE_PANEL); } getNavButton(listener): PanelNavButton { From 8621dc114265ab5399ac6d2ee450a6d9222cf7bf Mon Sep 17 00:00:00 2001 From: Holger Stitz Date: Fri, 13 Mar 2020 19:33:23 +0100 Subject: [PATCH 61/63] Increase LineUp panel width slightly This change fits the "Ranking Configuration" tab and the "Statistical Analysis" tabs (tourdino) next to each other. --- src/lineup/internal/panel/PanelTab.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lineup/internal/panel/PanelTab.ts b/src/lineup/internal/panel/PanelTab.ts index 56be8d053..27be14513 100644 --- a/src/lineup/internal/panel/PanelTab.ts +++ b/src/lineup/internal/panel/PanelTab.ts @@ -48,7 +48,7 @@ export class PanelTab extends EventHandler { readonly options: IPanelTabDesc = { cssClass: 'fa fa-sliders', title: 'Ranking Configuration', - width: '21em', + width: '23em', order: 0 }; private navButton: PanelNavButton; From 7e17290d06ff1ad738c2ae91236f179e8182ed49 Mon Sep 17 00:00:00 2001 From: Holger Stitz Date: Fri, 13 Mar 2020 19:37:07 +0100 Subject: [PATCH 62/63] Extract language string --- src/assets/locales/en/tdp.json | 1 + src/lineup/internal/panel/PanelTab.ts | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/assets/locales/en/tdp.json b/src/assets/locales/en/tdp.json index 25eaa77cf..9a72f53b6 100644 --- a/src/assets/locales/en/tdp.json +++ b/src/assets/locales/en/tdp.json @@ -206,6 +206,7 @@ }, "LineupPanelActions": { + "rankingPanelTabTitle": "Ranking Configuration", "searchPlaceholder": "Add Column …", "addColumnButton": "Add Column", "collapseButton": "(Un)Collapse", diff --git a/src/lineup/internal/panel/PanelTab.ts b/src/lineup/internal/panel/PanelTab.ts index 27be14513..f570eff8f 100644 --- a/src/lineup/internal/panel/PanelTab.ts +++ b/src/lineup/internal/panel/PanelTab.ts @@ -2,6 +2,7 @@ import {SidePanel, SearchBox, IEngineRankingContext, IRenderContext, IRankingHea import {ISearchOption} from '../LineUpPanelActions'; import {EventHandler} from 'phovea_core/src/event'; import PanelButton, {PanelNavButton} from './PanelButton'; +import i18n from 'phovea_core/src/i18n'; /** * Interface for the options parameter of PanelTab @@ -47,7 +48,7 @@ export class PanelTab extends EventHandler { readonly node: HTMLElement; readonly options: IPanelTabDesc = { cssClass: 'fa fa-sliders', - title: 'Ranking Configuration', + title: i18n.t('tdp:core.lineup.LineupPanelActions.rankingPanelTabTitle'), width: '23em', order: 0 }; From 1f3d7acd37679f7d805774ef5c97dfb563ba183f Mon Sep 17 00:00:00 2001 From: Holger Stitz Date: Fri, 13 Mar 2020 19:38:51 +0100 Subject: [PATCH 63/63] Fix side panel scrolling Before the the whole tdpView was scrolling when the content of the side panel was too long. Now only the tab panel content ist scrolling, but the tabs and the header remain fixed. --- src/styles/_view_lineup.scss | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/styles/_view_lineup.scss b/src/styles/_view_lineup.scss index a6e28aaf9..0c919acf2 100644 --- a/src/styles/_view_lineup.scss +++ b/src/styles/_view_lineup.scss @@ -104,6 +104,10 @@ $lu_assets: "~lineupjs/src/assets"; } } + .lu-side-panel { + width: 100%; + } + .lu-side-panel > main > section::before { content: "Column Summaries"; } @@ -115,6 +119,7 @@ $lu_assets: "~lineupjs/src/assets"; .lu-side-panel-wrapper { display: flex; flex-direction: column; + overflow: hidden; %lu-panel-button { background: #fff; @@ -273,15 +278,20 @@ $lu_assets: "~lineupjs/src/assets"; } .tab-content { + position: relative; margin-top: 0.5em; display: flex; flex-direction: column; flex: 1; - overflow: auto; } - .tab-pane.default { - overflow: hidden; + .tab-pane { + position: absolute; + top: 0; + bottom: 0; + right: 0; + left: 0; + overflow-y: auto; } }