diff --git a/src/components/Icon.js b/src/components/Icon.js
index c95c76d..ca64f43 100644
--- a/src/components/Icon.js
+++ b/src/components/Icon.js
@@ -1,6 +1,7 @@
import preact from 'preact'
/** @jsx preact.h */
+import Grabber from '../assets/icons/octicons/grabber.svg?svgr'
import Octoface from '../assets/icons/octicons/octoface.svg?svgr'
import ChevronDown from '../assets/icons/octicons/chevron-down.svg?svgr'
import TriangleRight from '../assets/icons/octicons/triangle-right.svg?svgr'
@@ -21,6 +22,8 @@ import cx from '../utils/cx'
function getSVGIconComponent(type) {
switch (type) {
+ case 'grabber':
+ return Grabber
case 'octoface':
return Octoface
case 'chevron-down':
diff --git a/src/components/ResizeHandler.js b/src/components/ResizeHandler.js
new file mode 100644
index 0000000..b95b5b0
--- /dev/null
+++ b/src/components/ResizeHandler.js
@@ -0,0 +1,60 @@
+import preact from 'preact'
+/** @jsx preact.h */
+
+import Icon from './Icon'
+
+export default class ResizeHandler extends preact.Component {
+ pointerDown = false
+ startX = 0
+ delta = 0
+ baseSize = this.props.baseSize
+
+ subscribeEvents = () => {
+ window.addEventListener('mousemove', this.onPointerMove)
+ window.addEventListener('mouseup', this.onPointerUp)
+ }
+
+ unsubscribeEvents = () => {
+ window.removeEventListener('mousemove', this.onPointerMove)
+ window.removeEventListener('mouseup', this.onPointerUp)
+ }
+
+ /**
+ *
+ * @param {MouseEvent} e
+ * @memberof ResizeHandler
+ */
+ onPointerDown = e => {
+ this.pointerDown = true
+ const { clientX } = e
+ this.startX = clientX
+ this.subscribeEvents()
+ }
+
+ onPointerMove = e => {
+ if (!this.pointerDown) return
+ const { clientX } = e
+ const { onResize } = this.props
+ this.delta = this.startX - clientX
+ onResize(this.delta + this.baseSize)
+ }
+
+ onPointerUp = () => {
+ this.pointerDown = false
+ this.baseSize += this.delta
+ this.unsubscribeEvents()
+ }
+
+ render() {
+ const { style } = this.props
+ return (
+
+
+
+ )
+ }
+}
\ No newline at end of file
diff --git a/src/components/SideBar.js b/src/components/SideBar.js
index d5b4b16..e0ea8e0 100644
--- a/src/components/SideBar.js
+++ b/src/components/SideBar.js
@@ -7,6 +7,7 @@ import FileExplorer from './FileExplorer'
import ToggleShowButton from './ToggleShowButton'
import MetaBar from './MetaBar'
import SettingsBar from './SettingsBar'
+import ResizeHandler from './ResizeHandler'
import cx from '../utils/cx'
import DOMHelper, { REPO_TYPE_PRIVATE } from '../utils/DOMHelper'
@@ -15,8 +16,12 @@ import storageHelper from '../utils/storageHelper'
import URLHelper from '../utils/URLHelper'
import keyHelper from '../utils/keyHelper'
+// initial width of side bar
+const baseSize = 260
export default class SideBar extends preact.Component {
state = {
+ // current width of side bar
+ size: 260,
// whether Gitako side bar should be shown
shouldShow: false,
// whether show settings pane
@@ -127,6 +132,12 @@ export default class SideBar extends preact.Component {
this.setState({ toggleShowSideBarShortcut: shortcut })
}
+ onResize = size => {
+ this.setState({
+ size,
+ })
+ }
+
renderAccessDeniedError() {
return (
@@ -155,6 +166,7 @@ export default class SideBar extends preact.Component {
render() {
const {
+ size,
shouldShow,
showSettings,
hasAccessToken,
@@ -166,16 +178,19 @@ export default class SideBar extends preact.Component {
-
- {this.renderContent()}
-
+
+
+
+ {this.renderContent()}
+
+
)
diff --git a/src/content.less b/src/content.less
index 7d5a4a2..a01577a 100644
--- a/src/content.less
+++ b/src/content.less
@@ -2,16 +2,17 @@
@name: gitako;
-@side-bar-width: 260px;
-@github-content-width: 1280px;
-@content-width: (@side-bar-width + @github-content-width);
+@min-screen-width: 1280px;
+@github-content-width: 1020px;
+@side-bar-base-width: (@min-screen-width - @github-content-width);
+@width-with-gitako: (@min-screen-width + @side-bar-base-width);
.with-@{name}-spacing {
- margin-left: @side-bar-width;
- @media (min-width: @github-content-width) and (max-width: @content-width) {
- margin-left: ~'calc(' @content-width ~' - 100vw)';
+ margin-left: @side-bar-base-width;
+ @media (min-width: @min-screen-width) and (max-width: @width-with-gitako) {
+ margin-left: ~'calc(' @width-with-gitako ~' - 100vw)';
}
- @media (min-width: @content-width) {
+ @media (min-width: @width-with-gitako) {
margin-left: 0;
}
}
@@ -68,10 +69,11 @@
}
.@{name}-toggle-show-button-wrapper {
- @animation-duration: 0.8s;
+ @animation-duration: 0.5s;
position: fixed;
top: 5px;
left: 0;
+ transform: translate((@side-bar-base-width - 30px));
z-index: 2;
display: inline-flex;
justify-content: center;
@@ -80,20 +82,23 @@
width: 30px;
height: 30px;
will-change: transform;
- transform: translateX(225px);
- transition: all @animation-duration ease;
border: 1px solid transparent;
- @media screen and (min-width: @content-width) {
- transform: translateX(~'calc((100vw - ' @content-width ~') / 2 + ' 225px ~')');
+
+ @media screen and (max-width: @width-with-gitako) {
+ transition: all @animation-duration ease;
+ }
+ @media screen and (min-width: @width-with-gitako) {
+ transition: border-color, border-radius, background @animation-duration ease;
+ transform: translate(~'calc(50vw - (' @github-content-width ~') / 2 - 30px)');
}
&.collapsed {
border-color: #999999;
- background: #f5f5f5;
border-radius: 3px;
+ background: #f5f5f5;
- @media screen and (max-width: @content-width) {
- transform: translateX(5px);
+ @media screen and (max-width: @width-with-gitako) {
+ transform: translate(5px);
}
.action-icon {
@@ -110,198 +115,229 @@
}
.@{name} {
- .@{name}-side-bar {
+ .@{name}-position-wrapper {
position: fixed;
top: 0;
left: 0;
- display: flex;
- flex-direction: column;
- width: @side-bar-width;
height: 100vh;
z-index: 2;
- background: #fafbfc;
- border-right: 1px solid #e1e4e8;
- overflow: hidden;
+ min-width: @side-bar-base-width;
+ @media screen and (min-width: @min-screen-width) {
+ width: ~'calc((100vw - ' @github-content-width ~') / 2)';
+ }
- @media screen and (min-width: @content-width) {
+ .@{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;
- left: ~'calc((100vw - ' @content-width ~') / 2)';
- }
- &.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;
-
- .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
in meta bar will be 'text'
- cursor: pointer;
- }
-
- .repo-name {
- font-weight: bolder;
- }
- }
-
- .description {
- padding: 4px 10px;
- }
-
- .file-explorer {
- width: 100%;
- height: 100%;
- outline: none;
+ @media screen and (min-width: @min-screen-width) {
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;
- }
- }
- }
- }
- }
-
- .@{name}-settings-bar {
- border-top: 1px solid #eaecef;
- &-content {
- padding: 0 10px;
- &-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;
- }
- }
- .placeholder-row {
- display: flex;
- justify-content: space-between;
align-items: center;
- padding: 6px 10px;
+ }
- .show-settings-icon {
- width: 20px;
- height: 20px;
- cursor: pointer;
+ .grabber-icon {
+ transform: rotate(90deg);
+ width: 12px;
+ }
+ }
+ .@{name}-side-bar {
+ position: absolute;
+ right: 0;
+ width: @side-bar-base-width;
+ max-width: 100%;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ background: #fafbfc;
+ border-right: 1px solid #e1e4e8;
+ overflow: hidden;
+ will-change: transform;
+ border-left: 1px solid #e1e4e8;
+
+ &.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;
+
+ .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 in meta bar will be 'text'
+ cursor: pointer;
+ }
+
+ .repo-name {
+ font-weight: bolder;
+ }
}
- .hide-settings-icon {
- width: 15px;
- height: 24px;
- 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;
+ }
+ }
+ }
+ }
+ }
+
+ .@{name}-settings-bar {
+ border-top: 1px solid #eaecef;
+ &-content {
+ padding: 0 10px;
+ &-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;
+ }
+ }
+ .placeholder-row {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 6px 10px;
+
+ .show-settings-icon {
+ width: 20px;
+ height: 20px;
+ cursor: pointer;
+ }
+ .hide-settings-icon {
+ width: 15px;
+ height: 24px;
+ cursor: pointer;
+ }
}
}
}