From d52cc6a46022f086563a4a0085ef2c582328f789 Mon Sep 17 00:00:00 2001 From: Ryan Baxley Date: Tue, 2 Oct 2018 12:22:17 -0400 Subject: [PATCH 1/3] Make tabs sortable --- .../graphql-playground-react/package.json | 1 + .../src/components/Playground/Tab.tsx | 7 +- .../src/components/Playground/TabBar.tsx | 108 +++++++++++++----- .../src/state/sessions/actions.ts | 2 + .../src/state/sessions/reducers.ts | 16 +++ packages/graphql-playground-react/yarn.lock | 10 +- 6 files changed, 109 insertions(+), 35 deletions(-) diff --git a/packages/graphql-playground-react/package.json b/packages/graphql-playground-react/package.json index 05d050bb8..42c2c8d4e 100644 --- a/packages/graphql-playground-react/package.json +++ b/packages/graphql-playground-react/package.json @@ -156,6 +156,7 @@ "react-modal": "^3.1.11", "react-redux": "^5.0.6", "react-router-dom": "^4.2.2", + "react-sortable-hoc": "^0.8.3", "react-transition-group": "^2.2.1", "react-virtualized": "^9.12.0", "redux": "^3.7.2", diff --git a/packages/graphql-playground-react/src/components/Playground/Tab.tsx b/packages/graphql-playground-react/src/components/Playground/Tab.tsx index f0f836b3e..9ccaf6351 100644 --- a/packages/graphql-playground-react/src/components/Playground/Tab.tsx +++ b/packages/graphql-playground-react/src/components/Playground/Tab.tsx @@ -154,17 +154,15 @@ const TabItem = withProps()(styled.div)` height: 43px; padding: 10px; padding-top: 9px; - margin-left: 10px; + margin-right: 10px; font-size: 14px; border-radius: 2px; border-bottom: 2px solid ${p => p.theme.editorColours.navigationBar}; box-sizing: border-box; cursor: pointer; + user-select: none; background: ${p => p.active ? p.theme.editorColours.tab : p.theme.editorColours.tabInactive}; - &:first-child { - margin-left: 0; - } &:hover { background: ${p => p.theme.editorColours.tab}; .close { @@ -202,6 +200,7 @@ const Icons = withProps()(styled.div)` const QueryTypes = styled.div` display: flex; + color: white; ` const QueryType = styled.div` diff --git a/packages/graphql-playground-react/src/components/Playground/TabBar.tsx b/packages/graphql-playground-react/src/components/Playground/TabBar.tsx index c959ed736..8c021d004 100644 --- a/packages/graphql-playground-react/src/components/Playground/TabBar.tsx +++ b/packages/graphql-playground-react/src/components/Playground/TabBar.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import { styled, withProps } from '../../styled' import { Icon } from 'graphcool-styles' -import Tab from './Tab' +import Tab, { Props as TabProps } from './Tab' import { connect } from 'react-redux' import { createStructuredSelector } from 'reselect' import { @@ -9,6 +9,13 @@ import { getSelectedSessionIdFromRoot, } from '../../state/sessions/selectors' import { Session } from '../../state/sessions/reducers' +import { reorderTabs } from '../../state/sessions/actions' +import { + SortableContainer, + SortableElement, + SortStart, + SortEnd, +} from 'react-sortable-hoc' export interface Props { onNewSession: any @@ -18,42 +25,82 @@ export interface Props { export interface ReduxProps { sessions: Session[] selectedSessionId: string + reorderTabs: (src: number, dest: number) => void +} + +interface State { + sorting: boolean } -const TabBar = ({ - sessions, - isApp, - selectedSessionId, - onNewSession, -}: Props & ReduxProps) => ( - - - {sessions.map(session => ( - - ))} - - - - - -) +const SortableTab = SortableElement(Tab) + +class TabBar extends React.PureComponent { + state = { sorting: false } + + render() { + const { sessions, isApp, selectedSessionId, onNewSession } = this.props + const { sorting } = this.state + return ( + + + {sessions.map((session, ndx) => ( + + ))} + {!sorting && ( + + + + )} + + + ) + } + + private onSortStart = ({ index }: SortStart) => { + this.setState({ sorting: true }) + } + + private onSortEnd = ({ oldIndex, newIndex }: SortEnd) => { + this.props.reorderTabs(oldIndex, newIndex) + this.setState({ sorting: false }) + } + + private getHelperDimensions = ({ node }: SortStart) => { + const { width, height } = node.getBoundingClientRect() + return { width, height } + } +} const mapStateToProps = createStructuredSelector({ sessions: getSessionsArray, selectedSessionId: getSelectedSessionIdFromRoot, }) -export default connect(mapStateToProps)(TabBar) +export default connect( + mapStateToProps, + { reorderTabs }, +)(TabBar) const StyledTabBar = styled.div` color: white; @@ -66,6 +113,8 @@ const StyledTabBar = styled.div` } ` +const SortableTabBar = SortableContainer(StyledTabBar) + interface TabsProps { isApp?: boolean } @@ -82,7 +131,6 @@ const Plus = styled.div` display: flex; height: 43px; width: 43px; - margin-left: 10px; border-radius: 2px; border-bottom: 2px solid ${p => p.theme.editorColours.navigationBar}; background: ${p => p.theme.editorColours.tabInactive}; diff --git a/packages/graphql-playground-react/src/state/sessions/actions.ts b/packages/graphql-playground-react/src/state/sessions/actions.ts index 8a48a4052..02c64a5a0 100644 --- a/packages/graphql-playground-react/src/state/sessions/actions.ts +++ b/packages/graphql-playground-react/src/state/sessions/actions.ts @@ -63,6 +63,7 @@ export const { setCurrentQueryEndTime, refetchSchema, setScrollTop, + reorderTabs, } = createActions({ // simple property setting EDIT_QUERY: query => ({ query }), @@ -179,6 +180,7 @@ export const { SELECT_TAB: simpleAction('sessionId'), SELECT_TAB_INDEX: simpleAction('index'), CLOSE_TAB: simpleAction('sessionId'), + REORDER_TABS: (src, dest) => ({ src, dest }), // files, settings, config EDIT_SETTINGS: simpleAction(), diff --git a/packages/graphql-playground-react/src/state/sessions/reducers.ts b/packages/graphql-playground-react/src/state/sessions/reducers.ts index 71d0c7ec0..8f39d6612 100644 --- a/packages/graphql-playground-react/src/state/sessions/reducers.ts +++ b/packages/graphql-playground-react/src/state/sessions/reducers.ts @@ -26,6 +26,7 @@ import { getSelectedSessionId } from './selectors' import { getDefaultSession, defaultQuery } from '../../constants' import * as cuid from 'cuid' import { formatError } from './fetchingSagas' +import { arrayMove } from 'react-sortable-hoc' export interface SessionStateProps { sessions: OrderedMap @@ -526,6 +527,21 @@ const reducer = handleActions( state.sessions.size - 1, ) }, + REORDER_TABS: (state, { payload: { src, dest } }) => { + const seq = state.sessions.toIndexedSeq() + + const indexes: number[] = [] + for (let i = 0; i < seq.size; i++) indexes.push(i) + const newIndexes = arrayMove(indexes, src, dest) + + let newSessions = OrderedMap() + for (let i = 0; i < seq.size; i++) { + const ndx = newIndexes[i] + const val = seq.get(ndx) + newSessions = newSessions.set(val.id, val) + } + return state.set('sessions', newSessions) + }, EDIT_SETTINGS: state => { return state.setIn( ['sessions', getSelectedSessionId(state), 'changed'], diff --git a/packages/graphql-playground-react/yarn.lock b/packages/graphql-playground-react/yarn.lock index c4144d4da..084c3e0b0 100644 --- a/packages/graphql-playground-react/yarn.lock +++ b/packages/graphql-playground-react/yarn.lock @@ -1089,7 +1089,7 @@ babel-register@^6.11.6, babel-register@^6.26.0: mkdirp "^0.5.1" source-map-support "^0.4.15" -babel-runtime@^6.18.0, babel-runtime@^6.20.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0: +babel-runtime@^6.11.6, babel-runtime@^6.18.0, babel-runtime@^6.20.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" dependencies: @@ -7270,6 +7270,14 @@ react-side-effect@^1.1.0: exenv "^1.2.1" shallowequal "^1.0.1" +react-sortable-hoc@^0.8.3: + version "0.8.3" + resolved "https://registry.yarnpkg.com/react-sortable-hoc/-/react-sortable-hoc-0.8.3.tgz#8537e8ab8d6bad6829885755a0f847817ed78648" + dependencies: + babel-runtime "^6.11.6" + invariant "^2.2.1" + prop-types "^15.5.7" + react-test-renderer@^16.0.0-0: version "16.4.1" resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.4.1.tgz#f2fb30c2c7b517db6e5b10ed20bb6b0a7ccd8d70" From b4b5b6f02af7f2c7acfd6444fa255fdec3ed27d4 Mon Sep 17 00:00:00 2001 From: Ryan Baxley Date: Wed, 3 Oct 2018 19:29:12 -0400 Subject: [PATCH 2/3] Change tab selection from onClick to onMouseDown --- .../graphql-playground-react/src/components/Playground/Tab.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/graphql-playground-react/src/components/Playground/Tab.tsx b/packages/graphql-playground-react/src/components/Playground/Tab.tsx index 9ccaf6351..9f1561e53 100644 --- a/packages/graphql-playground-react/src/components/Playground/Tab.tsx +++ b/packages/graphql-playground-react/src/components/Playground/Tab.tsx @@ -45,7 +45,7 @@ class Tab extends React.PureComponent { 'New Tab' return ( - + {session.subscriptionActive && } From 2bbccaa82a478cfe5bc15e81761270c7446b726e Mon Sep 17 00:00:00 2001 From: Ryan Baxley Date: Wed, 3 Oct 2018 19:51:38 -0400 Subject: [PATCH 3/3] Allow scrolling of tabbar while sorting tabs --- .../src/components/Playground/TabBar.tsx | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/packages/graphql-playground-react/src/components/Playground/TabBar.tsx b/packages/graphql-playground-react/src/components/Playground/TabBar.tsx index 8c021d004..2736de0f1 100644 --- a/packages/graphql-playground-react/src/components/Playground/TabBar.tsx +++ b/packages/graphql-playground-react/src/components/Playground/TabBar.tsx @@ -48,7 +48,6 @@ class TabBar extends React.PureComponent { axis="x" lockAxis="x" lockToContainerEdges={true} - lockOffset="0px" distance={10} transitionDuration={200} > @@ -61,17 +60,15 @@ class TabBar extends React.PureComponent { index={ndx} /> ))} - {!sorting && ( - - - - )} + + + ) @@ -126,9 +123,14 @@ const Tabs = withProps()(styled.div)` padding-left: ${p => (p.isApp ? '43px' : '0')}; ` -const Plus = styled.div` +interface PlusProps { + sorting: boolean +} + +const Plus = withProps()(styled.div)` box-sizing: border-box; display: flex; + visibility: ${p => (p.sorting ? 'hidden' : 'visible')} height: 43px; width: 43px; border-radius: 2px;