refactor: create <Resizable /> and root component <Gitako />

This commit is contained in:
EnixCoda 2018-06-30 15:13:11 +08:00
parent ec102c9d6a
commit bae918302e
10 changed files with 287 additions and 248 deletions

14
src/components/Gitako.js Normal file
View file

@ -0,0 +1,14 @@
import React from 'react'
import SideBar from './SideBar'
import { Gitako as GitakoCore } from '../driver/core'
import connect from '../driver/connect'
@connect(GitakoCore)
export default class Gitako extends React.PureComponent {
render() {
return (
<SideBar />
)
}
}

View file

@ -0,0 +1,29 @@
import React from 'react'
import PropTypes from 'prop-types'
import ResizeHandler from './ResizeHandler'
import cx from '../utils/cx';
export default class Resizable extends React.PureComponent {
static propTypes = {
baseSize: PropTypes.number.isRequired,
}
state = {
size: this.props.baseSize,
}
onResize = size => this.setState({ size })
render() {
const { className, children } = this.props
const { size } = this.state
return (
<div className={cx('gitako-position-wrapper', className)}>
<ResizeHandler onResize={this.onResize} size={size} />
<div className={'gitako-position-content'} style={{ width: size }}>
{children}
</div>
</div>
)
}
}

View file

@ -6,7 +6,7 @@ export default class ResizeHandler extends React.PureComponent {
pointerDown = false
startX = 0
delta = 0
baseSize = this.props.baseSize
baseSize = this.props.size
subscribeEvents = () => {
window.addEventListener('mousemove', this.onPointerMove)

View file

@ -8,20 +8,16 @@ import FileExplorer from './FileExplorer'
import ToggleShowButton from './ToggleShowButton'
import MetaBar from './MetaBar'
import SettingsBar from './SettingsBar'
import ResizeHandler from './ResizeHandler'
import Portal from './Portal'
import Resizable from './Resizable'
import cx from '../utils/cx'
const baseSize = 260
@connect(SideBarCore)
export default class Gitako extends React.PureComponent {
static propTypes = {
// initial width of side bar
baseSize: PropTypes.number,
// current width of side bar
size: PropTypes.number,
// whether Gitako side bar should be shown
shouldShow: PropTypes.bool,
// whether show settings pane
@ -47,13 +43,11 @@ export default class Gitako extends React.PureComponent {
onAccessTokenChange: PropTypes.func.isRequired,
onKeyDown: PropTypes.func.isRequired,
onShortcutChange: PropTypes.func.isRequired,
onResize: PropTypes.func.isRequired,
setMetaData: PropTypes.func.isRequired,
}
static defaultProps = {
baseSize,
size: baseSize,
baseSize: 260,
shouldShow: false,
showSettings: false,
errorDueToAuth: false,
@ -110,7 +104,7 @@ export default class Gitako extends React.PureComponent {
render() {
const {
size,
baseSize,
shouldShow,
showSettings,
accessToken,
@ -118,20 +112,18 @@ export default class Gitako extends React.PureComponent {
toggleShowSideBarShortcut,
logoContainerElement,
toggleShowSideBar,
onResize,
toggleShowSettings,
onShortcutChange,
onAccessTokenChange,
setCompressSingleton,
} = this.props
return (
<div className={cx('gitako', { hidden: !shouldShow })}>
<div className={'gitako-side-bar'}>
<Portal into={logoContainerElement}>
<ToggleShowButton shouldShow={shouldShow} toggleShowSideBar={toggleShowSideBar} />
</Portal>
<div className={'gitako-position-wrapper'}>
<ResizeHandler onResize={onResize} baseSize={baseSize} style={{ right: size }} />
<div className={'gitako-side-bar'} style={{ width: size }}>
<Resizable className={cx({ hidden: !shouldShow })} baseSize={baseSize}>
<div className={'gitako-side-bar-body'}>
{this.renderContent()}
<SettingsBar
toggleShowSettings={toggleShowSettings}
@ -142,9 +134,9 @@ export default class Gitako extends React.PureComponent {
compressSingletonFolder={compressSingletonFolder}
toggleShowSideBarShortcut={toggleShowSideBarShortcut}
setCompressSingleton={setCompressSingleton}
/>
/>
</div>
</div>
</Resizable>
</div>
)
}

View file

@ -1,11 +1,11 @@
import React from 'react'
import ReactDOM from 'react-dom'
import SideBar from './components/SideBar'
import Gitako from './components/Gitako'
import './content.less'
const SideBarElement = document.createElement('div')
document.body.appendChild(SideBarElement)
ReactDOM.render(<SideBar />, SideBarElement)
ReactDOM.render(<Gitako />, SideBarElement)

View file

@ -111,7 +111,7 @@
}
}
.@{name} {
.@{name}-side-bar {
.@{name}-position-wrapper {
position: fixed;
top: 0;
@ -119,255 +119,253 @@
height: 100vh;
z-index: 2;
min-width: @side-bar-base-width;
display: flex;
justify-content: flex-end;
@media screen and (min-width: @min-screen-width) {
width: ~'calc((100vw - ' @github-content-width ~') / 2)';
}
.@{name}-resize-handler {
@resize-handler-width: 16px;
@side-bar-and-handler-width: (@min-screen-width + @resize-handler-width);
position: absolute;
top: 0;
display: none;
width: @resize-handler-width;
height: 100vh;
background: #fafbfc;
cursor: ew-resize;
user-select: none;
border-left: 1px solid #e1e4e8;
@media screen and (min-width: @min-screen-width) {
display: flex;
align-items: center;
}
.grabber-icon {
transform: rotate(90deg);
width: 12px;
}
}
.@{name}-side-bar {
position: absolute;
right: 0;
width: @side-bar-base-width;
.gitako-position-content {
max-width: 100%;
height: 100%;
}
}
.@{name}-resize-handler {
@resize-handler-width: 16px;
@side-bar-and-handler-width: (@min-screen-width + @resize-handler-width);
display: none;
width: @resize-handler-width;
background: #fafbfc;
cursor: ew-resize;
user-select: none;
border-left: 1px solid #e1e4e8;
@media screen and (min-width: @min-screen-width) {
display: flex;
align-items: center;
}
.grabber-icon {
transform: rotate(90deg);
width: 12px;
}
}
.@{name}-side-bar-body {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
background: #fafbfc;
border-right: 1px solid #e1e4e8;
border-left: 1px solid #e1e4e8;
overflow: hidden;
&.hidden {
.hidden;
}
.octicon {
transition: 0.3s ease;
color: rgba(3, 47, 98, 0.55);
vertical-align: initial;
}
.octicon-wrapper {
display: inline-block;
width: 16px;
height: 16px;
text-align: center;
vertical-align: middle;
}
.octicon-color {
color: rgba(3, 47, 98, 0.55);
}
.@{name}-side-bar-content {
display: flex;
flex: 1;
flex-direction: column;
background: #fafbfc;
border-right: 1px solid #e1e4e8;
overflow: hidden;
will-change: transform;
border-left: 1px solid #e1e4e8;
&.hidden {
.hidden;
}
.meta-bar {
position: relative;
padding: 10px;
padding-right: 30px;
font-size: 13px;
line-height: 20px;
color: #586069;
background-color: #f1f8ff;
border-bottom: 1px solid #c8e1ff;
.octicon {
transition: 0.3s ease;
color: rgba(3, 47, 98, 0.55);
vertical-align: initial;
}
.octicon-wrapper {
display: inline-block;
width: 16px;
height: 16px;
text-align: center;
vertical-align: middle;
}
.octicon-color {
color: rgba(3, 47, 98, 0.55);
}
.@{name}-side-bar-content {
display: flex;
flex: 1;
flex-direction: column;
.meta-bar {
position: relative;
padding: 10px;
padding-right: 30px;
font-size: 13px;
line-height: 20px;
color: #586069;
background-color: #f1f8ff;
border-bottom: 1px solid #c8e1ff;
a {
// fix a weird bug:
// when gitako failed loading repo, cursor hovering <a> in meta bar will be 'text'
cursor: pointer;
}
.repo-name {
font-weight: bolder;
}
a {
// fix a weird bug:
// when gitako failed loading repo, cursor hovering <a> in meta bar will be 'text'
cursor: pointer;
}
.description {
padding: 4px 10px;
}
.file-explorer {
width: 100%;
height: 100%;
outline: none;
display: flex;
flex-direction: column;
flex-wrap: nowrap;
&.freeze {
filter: blur(1.5px) opacity(0.6) grayscale(0.9);
pointer-events: none;
}
/* search input */
.search-input-wrapper {
input[type='text'].form-control {
box-shadow: none;
width: 100%;
}
}
.no-results {
padding: 0px 10px;
color: #666;
}
.files {
flex: 1;
overflow: auto;
}
.node-item-row {
background: #fff;
&:hover {
background: #f6f8fa;
}
&.focused {
background: #f0f0f6;
}
.node-item {
word-wrap: normal;
word-break: break-all;
margin: 0;
color: #0366d6;
line-height: 20px;
padding: 6px 0;
cursor: pointer;
border-top: 1px solid #eaecef;
transition: all 0.5s ease;
// folder icon rotate when expand
&.expanded .octicon.octicon-triangle-right {
transform: rotate(90deg);
}
.node-item-name {
padding-left: 6px;
vertical-align: middle;
}
.node-item:hover .node-item-name {
text-decoration: underline;
}
}
}
.repo-name {
font-weight: bolder;
}
}
.@{name}-settings-bar {
z-index: 2;
background: #fafbfc;
.description {
padding: 4px 10px;
}
.file-explorer {
width: 100%;
height: 100%;
outline: none;
display: flex;
flex-direction: column;
&-title {
border-top: 1px solid #eaecef;
padding: 6px 10px;
box-shadow: 0 1px 8px 0 rgba(0,0,0,.2), 0 3px 4px 0 rgba(0,0,0,.14), 0 3px 3px -2px rgba(0,0,0,.12);
z-index: 1;
flex-wrap: nowrap;
&.freeze {
filter: blur(1.5px) opacity(0.6) grayscale(0.9);
pointer-events: none;
}
&-content {
padding: 0 10px;
/* search input */
.search-input-wrapper {
input[type='text'].form-control {
box-shadow: none;
width: 100%;
}
}
.no-results {
padding: 0px 10px;
color: #666;
}
.files {
flex: 1;
overflow: auto;
position: relative;
.shadow-shelter {
position: absolute;
width: 100%;
height: 6px;
left: 0;
background: #fafbfc;
z-index: 1;
}
&-section {
padding-bottom: 10px;
}
}
.access-token {
border-bottom: none; // prevent overwrite by github style
.hint {
color: #6a737d;
}
}
.access-token-input-control {
display: flex;
margin-top: 8px;
.access-token-input {
flex: 1;
box-shadow: none;
&:disabled {
cursor: not-allowed;
.node-item-row {
background: #fff;
&:hover {
background: #f6f8fa;
}
&.focused {
background: #f0f0f6;
}
.node-item {
word-wrap: normal;
word-break: break-all;
margin: 0;
color: #0366d6;
line-height: 20px;
padding: 6px 0;
cursor: pointer;
border-top: 1px solid #eaecef;
transition: all 0.5s ease;
// folder icon rotate when expand
&.expanded .octicon.octicon-triangle-right {
transform: rotate(90deg);
}
.node-item-name {
padding-left: 6px;
vertical-align: middle;
}
.node-item:hover .node-item-name {
text-decoration: underline;
}
}
}
.toggle-shortcut {
.hint {
color: #6a737d;
}
}
.toggle-shortcut-input-control {
display: flex;
margin-top: 8px;
.toggle-shortcut-input {
flex: 1;
box-shadow: none;
}
}
.singleton {
.hint {
color: #6a737d;
}
}
.placeholder-row {
flex-shrink: 0;
display: flex;
justify-content: space-between;
align-items: center;
padding: 6px 10px;
border-top: 1px solid #eaecef;
}
}
.version {
color: #999999;
}
.show-settings-icon {
width: 20px;
height: 20px;
cursor: pointer;
}
.hide-settings-icon {
width: 20px;
height: 20px;
cursor: pointer;
.@{name}-settings-bar {
z-index: 2;
background: #fafbfc;
display: flex;
flex-direction: column;
&-title {
border-top: 1px solid #eaecef;
padding: 6px 10px;
box-shadow: 0 1px 8px 0 rgba(0,0,0,.2), 0 3px 4px 0 rgba(0,0,0,.14), 0 3px 3px -2px rgba(0,0,0,.12);
z-index: 1;
}
&-content {
padding: 0 10px;
flex: 1;
overflow: auto;
position: relative;
.shadow-shelter {
position: absolute;
width: 100%;
height: 6px;
left: 0;
background: #fafbfc;
z-index: 1;
}
&-section {
padding-bottom: 10px;
}
}
.access-token {
border-bottom: none; // prevent overwrite by github style
.hint {
color: #6a737d;
}
}
.access-token-input-control {
display: flex;
margin-top: 8px;
.access-token-input {
flex: 1;
box-shadow: none;
&:disabled {
cursor: not-allowed;
}
}
}
.toggle-shortcut {
.hint {
color: #6a737d;
}
}
.toggle-shortcut-input-control {
display: flex;
margin-top: 8px;
.toggle-shortcut-input {
flex: 1;
box-shadow: none;
}
}
.singleton {
.hint {
color: #6a737d;
}
}
.placeholder-row {
flex-shrink: 0;
display: flex;
justify-content: space-between;
align-items: center;
padding: 6px 10px;
border-top: 1px solid #eaecef;
.version {
color: #999999;
}
.show-settings-icon {
width: 20px;
height: 20px;
cursor: pointer;
}
.hide-settings-icon {
width: 20px;
height: 20px;
cursor: pointer;
}
}
}
}
}

View file

@ -0,0 +1,9 @@
import DOMHelper from '../../utils/DOMHelper'
const init = dispatch => () => {
DOMHelper.decorateGitHubPageContent()
}
export default {
init,
}

View file

@ -6,7 +6,6 @@ import keyHelper from '../../utils/keyHelper'
const init = dispatch => async () => {
try {
DOMHelper.decorateGitHubPageContent()
const metaData = URLHelper.parse()
dispatch(setMetaData, metaData)
const { access_token: accessToken, shortcut, compressSingletonFolder } = await configHelper.get()
@ -21,7 +20,7 @@ const init = dispatch => async () => {
DOMHelper.mountTopProgressBar()
}
const treeData = await GitHubHelper.getTreeData({ ...metaData, accessToken })
dispatch({ logoContainerElement: DOMHelper.insertLogo() })
dispatch({ logoContainerElement: DOMHelper.insertLogoMountPoint() })
dispatch({ treeData })
if (shouldShow) {
DOMHelper.unmountTopProgressBar()
@ -72,8 +71,6 @@ const setShouldShow = dispatch => shouldShow => {
DOMHelper.setBodyIndent(shouldShow)
}
const onResize = dispatch => size => dispatch({ size })
const toggleShowSettings = dispatch => () => dispatch(({ showSettings }) => ({ showSettings: !showSettings }))
const onAccessTokenChange = dispatch => accessToken => dispatch({ accessToken })
@ -93,7 +90,6 @@ export default {
toggleShowSettings,
onAccessTokenChange,
onShortcutChange,
onResize,
setMetaData,
setCompressSingleton,
}

View file

@ -1,2 +1,3 @@
export { default as Gitako } from './Gitako'
export { default as SideBar } from './SideBar'
export { default as FileExplorer } from './FileExplorer'

View file

@ -24,7 +24,7 @@ function setBodyIndent(shouldShowGitako) {
* add the logo element into DOM
*
*/
function insertLogo() {
function insertLogoMountPoint() {
const logoSelector = '.gitako .gitako-logo'
const logoElement = document.querySelector(logoSelector)
if (logoElement) {
@ -329,7 +329,7 @@ export default {
focusFileExplorer,
getCurrentPageType,
getRepoPageType,
insertLogo,
insertLogoMountPoint,
setBodyIndent,
scrollToNodeElement,
scrollToRepoContent,