From 92e9f02e21f384fe7a81556bf3cf0468a404bb0a Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Wed, 14 Feb 2018 15:54:18 +0900
Subject: [PATCH] wip

---
 .../components/drive.vue}                     | 826 +++++++++---------
 1 file changed, 400 insertions(+), 426 deletions(-)
 rename src/web/app/desktop/{-tags/drive/browser.tag => views/components/drive.vue} (51%)

diff --git a/src/web/app/desktop/-tags/drive/browser.tag b/src/web/app/desktop/views/components/drive.vue
similarity index 51%
rename from src/web/app/desktop/-tags/drive/browser.tag
rename to src/web/app/desktop/views/components/drive.vue
index 7aaedab822..5d398dab98 100644
--- a/src/web/app/desktop/-tags/drive/browser.tag
+++ b/src/web/app/desktop/views/components/drive.vue
@@ -1,6 +1,7 @@
-<mk-drive-browser>
+<template>
+<div class="mk-drive">
 	<nav>
-		<div class="path" oncontextmenu={ pathOncontextmenu }>
+		<div class="path" @contextmenu.prevent.stop="() => {}">
 			<mk-drive-browser-nav-folder :class="{ current: folder == null }" folder={ null }/>
 			<template each={ folder in hierarchyFolders }>
 				<span class="separator">%fa:angle-right%</span>
@@ -11,7 +12,15 @@
 		</div>
 		<input class="search" type="search" placeholder="&#xf002; %i18n:desktop.tags.mk-drive-browser.search%"/>
 	</nav>
-	<div class="main { uploading: uploads.length > 0, fetching: fetching }" ref="main" onmousedown={ onmousedown } ondragover={ ondragover } ondragenter={ ondragenter } ondragleave={ ondragleave } ondrop={ ondrop } oncontextmenu={ oncontextmenu }>
+	<div class="main { uploading: uploads.length > 0, fetching: fetching }"
+		ref="main"
+		@mousedown="onMousedown"
+		@dragover.prevent.stop="onDragover"
+		@dragenter.prevent="onDragenter"
+		@dragleave="onDragleave"
+		@drop.prevent.stop="onDrop"
+		@contextmenu.prevent.stop="onContextmenu"
+	>
 		<div class="selection" ref="selection"></div>
 		<div class="contents" ref="contents">
 			<div class="folders" ref="foldersContainer" v-if="folders.length > 0">
@@ -44,323 +53,142 @@
 		</div>
 	</div>
 	<div class="dropzone" v-if="draghover"></div>
-	<mk-uploader ref="uploader"/>
-	<input ref="fileInput" type="file" accept="*/*" multiple="multiple" tabindex="-1" onchange={ changeFileInput }/>
-	<style lang="stylus" scoped>
-		:scope
-			display block
+	<mk-uploader @change="onChangeUploaderUploads" @uploaded="onUploaderUploaded"/>
+	<input ref="fileInput" type="file" accept="*/*" multiple="multiple" tabindex="-1" @change="onChangeFileInput"/>
+</div>
+</template>
 
-			> nav
-				display block
-				z-index 2
-				width 100%
-				overflow auto
-				font-size 0.9em
-				color #555
-				background #fff
-				//border-bottom 1px solid #dfdfdf
-				box-shadow 0 1px 0 rgba(0, 0, 0, 0.05)
+<script lang="ts">
+import Vue from 'vue';
+import contains from '../../../common/scripts/contains';
+import dialog from '../../scripts/dialog';
+import inputDialog from '../../scripts/input-dialog';
 
-				&, *
-					user-select none
+export default Vue.extend({
+	props: {
+		initFolder: {
+			required: false
+		},
+		multiple: {
+			default: false
+		}
+	},
+	data() {
+		return {
+			/**
+			 * 現在の階層(フォルダ)
+			 * * null でルートを表す
+			 */
+			folder: null,
 
-				> .path
-					display inline-block
-					vertical-align bottom
-					margin 0
-					padding 0 8px
-					width calc(100% - 200px)
-					line-height 38px
-					white-space nowrap
+			files: [],
+			folders: [],
+			moreFiles: false,
+			moreFolders: false,
+			hierarchyFolders: [],
+			selectedFiles: [],
+			uploadings: [],
+			connection: null,
+			connectionId: null,
 
-					> *
-						display inline-block
-						margin 0
-						padding 0 8px
-						line-height 38px
-						cursor pointer
+			/**
+			 * ドロップされようとしているか
+			 */
+			draghover: false,
 
-						i
-							margin-right 4px
+			/**
+			 * 自信の所有するアイテムがドラッグをスタートさせたか
+			 * (自分自身の階層にドロップできないようにするためのフラグ)
+			 */
+			isDragSource: false,
 
-						*
-							pointer-events none
-
-						&:hover
-							text-decoration underline
-
-						&.current
-							font-weight bold
-							cursor default
-
-							&:hover
-								text-decoration none
-
-						&.separator
-							margin 0
-							padding 0
-							opacity 0.5
-							cursor default
-
-							> [data-fa]
-								margin 0
-
-				> .search
-					display inline-block
-					vertical-align bottom
-					user-select text
-					cursor auto
-					margin 0
-					padding 0 18px
-					width 200px
-					font-size 1em
-					line-height 38px
-					background transparent
-					outline none
-					//border solid 1px #ddd
-					border none
-					border-radius 0
-					box-shadow none
-					transition color 0.5s ease, border 0.5s ease
-					font-family FontAwesome, sans-serif
-
-					&[data-active='true']
-						background #fff
-
-					&::-webkit-input-placeholder,
-					&:-ms-input-placeholder,
-					&:-moz-placeholder
-						color $ui-control-foreground-color
-
-			> .main
-				padding 8px
-				height calc(100% - 38px)
-				overflow auto
-
-				&, *
-					user-select none
-
-				&.fetching
-					cursor wait !important
-
-					*
-						pointer-events none
-
-					> .contents
-						opacity 0.5
-
-				&.uploading
-					height calc(100% - 38px - 100px)
-
-				> .selection
-					display none
-					position absolute
-					z-index 128
-					top 0
-					left 0
-					border solid 1px $theme-color
-					background rgba($theme-color, 0.5)
-					pointer-events none
-
-				> .contents
-
-					> .folders
-					> .files
-						display flex
-						flex-wrap wrap
-
-						> .folder
-						> .file
-							flex-grow 1
-							width 144px
-							margin 4px
-
-						> .padding
-							flex-grow 1
-							pointer-events none
-							width 144px + 8px // 8px is margin
-
-					> .empty
-						padding 16px
-						text-align center
-						color #999
-						pointer-events none
-
-						> p
-							margin 0
-
-				> .fetching
-					.spinner
-						margin 100px auto
-						width 40px
-						height 40px
-						text-align center
-
-						animation sk-rotate 2.0s infinite linear
-
-					.dot1, .dot2
-						width 60%
-						height 60%
-						display inline-block
-						position absolute
-						top 0
-						background-color rgba(0, 0, 0, 0.3)
-						border-radius 100%
-
-						animation sk-bounce 2.0s infinite ease-in-out
-
-					.dot2
-						top auto
-						bottom 0
-						animation-delay -1.0s
-
-					@keyframes sk-rotate { 100% { transform: rotate(360deg); }}
-
-					@keyframes sk-bounce {
-						0%, 100% {
-							transform: scale(0.0);
-						} 50% {
-							transform: scale(1.0);
-						}
-					}
-
-			> .dropzone
-				position absolute
-				left 0
-				top 38px
-				width 100%
-				height calc(100% - 38px)
-				border dashed 2px rgba($theme-color, 0.5)
-				pointer-events none
-
-			> mk-uploader
-				height 100px
-				padding 16px
-				background #fff
-
-			> input
-				display none
-
-	</style>
-	<script lang="typescript">
-		import contains from '../../../common/scripts/contains';
-		import dialog from '../../scripts/dialog';
-		import inputDialog from '../../scripts/input-dialog';
-
-		this.mixin('i');
-		this.mixin('api');
-
-		this.mixin('drive-stream');
-		this.connection = this.driveStream.getConnection();
-		this.connectionId = this.driveStream.use();
-
-		this.files = [];
-		this.folders = [];
-		this.hierarchyFolders = [];
-		this.selectedFiles = [];
-
-		this.uploads = [];
-
-		// 現在の階層(フォルダ)
-		// * null でルートを表す
-		this.folder = null;
-
-		this.multiple = this.opts.multiple != null ? this.opts.multiple : false;
-
-		// ドロップされようとしているか
-		this.draghover = false;
-
-		// 自信の所有するアイテムがドラッグをスタートさせたか
-		// (自分自身の階層にドロップできないようにするためのフラグ)
-		this.isDragSource = false;
-
-		this.on('mount', () => {
-			this.$refs.uploader.on('uploaded', file => {
-				this.addFile(file, true);
-			});
-
-			this.$refs.uploader.on('change-uploads', uploads => {
-				this.update({
-					uploads: uploads
-				});
-			});
-
-			this.connection.on('file_created', this.onStreamDriveFileCreated);
-			this.connection.on('file_updated', this.onStreamDriveFileUpdated);
-			this.connection.on('folder_created', this.onStreamDriveFolderCreated);
-			this.connection.on('folder_updated', this.onStreamDriveFolderUpdated);
-
-			if (this.opts.folder) {
-				this.move(this.opts.folder);
-			} else {
-				this.fetch();
-			}
-		});
-
-		this.on('unmount', () => {
-			this.connection.off('file_created', this.onStreamDriveFileCreated);
-			this.connection.off('file_updated', this.onStreamDriveFileUpdated);
-			this.connection.off('folder_created', this.onStreamDriveFolderCreated);
-			this.connection.off('folder_updated', this.onStreamDriveFolderUpdated);
-			this.driveStream.dispose(this.connectionId);
-		});
-
-		this.onStreamDriveFileCreated = file => {
-			this.addFile(file, true);
+			fetching: true
 		};
+	},
+	mounted() {
+		this.connection = this.$root.$data.os.streams.driveStream.getConnection();
+		this.connectionId = this.$root.$data.os.streams.driveStream.use();
 
-		this.onStreamDriveFileUpdated = file => {
+		this.connection.on('file_created', this.onStreamDriveFileCreated);
+		this.connection.on('file_updated', this.onStreamDriveFileUpdated);
+		this.connection.on('folder_created', this.onStreamDriveFolderCreated);
+		this.connection.on('folder_updated', this.onStreamDriveFolderUpdated);
+
+		if (this.initFolder) {
+			this.move(this.initFolder);
+		} else {
+			this.fetch();
+		}
+	},
+	beforeDestroy() {
+		this.connection.off('file_created', this.onStreamDriveFileCreated);
+		this.connection.off('file_updated', this.onStreamDriveFileUpdated);
+		this.connection.off('folder_created', this.onStreamDriveFolderCreated);
+		this.connection.off('folder_updated', this.onStreamDriveFolderUpdated);
+		this.$root.$data.os.streams.driveStream.dispose(this.connectionId);
+	},
+	methods: {
+		onStreamDriveFileCreated(file) {
+			this.addFile(file, true);
+		},
+		onStreamDriveFileUpdated(file) {
 			const current = this.folder ? this.folder.id : null;
 			if (current != file.folder_id) {
 				this.removeFile(file);
 			} else {
 				this.addFile(file, true);
 			}
-		};
-
-		this.onStreamDriveFolderCreated = folder => {
+		},
+		onStreamDriveFolderCreated(folder) {
 			this.addFolder(folder, true);
-		};
-
-		this.onStreamDriveFolderUpdated = folder => {
+		},
+		onStreamDriveFolderUpdated(folder) {
 			const current = this.folder ? this.folder.id : null;
 			if (current != folder.parent_id) {
 				this.removeFolder(folder);
 			} else {
 				this.addFolder(folder, true);
 			}
-		};
-
-		this.onmousedown = e => {
+		},
+		onChangeUploaderUploads(uploads) {
+			this.uploadings = uploads;
+		},
+		onUploaderUploaded(file) {
+			this.addFile(file, true);
+		},
+		onMousedown(e): any {
 			if (contains(this.$refs.foldersContainer, e.target) || contains(this.$refs.filesContainer, e.target)) return true;
 
-			const rect = this.$refs.main.getBoundingClientRect();
+			const main = this.$refs.main as any;
+			const selection = this.$refs.selection as any;
 
-			const left = e.pageX + this.$refs.main.scrollLeft - rect.left - window.pageXOffset
-			const top = e.pageY + this.$refs.main.scrollTop - rect.top - window.pageYOffset
+			const rect = main.getBoundingClientRect();
+
+			const left = e.pageX + main.scrollLeft - rect.left - window.pageXOffset
+			const top = e.pageY + main.scrollTop - rect.top - window.pageYOffset
 
 			const move = e => {
-				this.$refs.selection.style.display = 'block';
+				selection.style.display = 'block';
 
-				const cursorX = e.pageX + this.$refs.main.scrollLeft - rect.left - window.pageXOffset;
-				const cursorY = e.pageY + this.$refs.main.scrollTop - rect.top - window.pageYOffset;
+				const cursorX = e.pageX + main.scrollLeft - rect.left - window.pageXOffset;
+				const cursorY = e.pageY + main.scrollTop - rect.top - window.pageYOffset;
 				const w = cursorX - left;
 				const h = cursorY - top;
 
 				if (w > 0) {
-					this.$refs.selection.style.width = w + 'px';
-					this.$refs.selection.style.left = left + 'px';
+					selection.style.width = w + 'px';
+					selection.style.left = left + 'px';
 				} else {
-					this.$refs.selection.style.width = -w + 'px';
-					this.$refs.selection.style.left = cursorX + 'px';
+					selection.style.width = -w + 'px';
+					selection.style.left = cursorX + 'px';
 				}
 
 				if (h > 0) {
-					this.$refs.selection.style.height = h + 'px';
-					this.$refs.selection.style.top = top + 'px';
+					selection.style.height = h + 'px';
+					selection.style.top = top + 'px';
 				} else {
-					this.$refs.selection.style.height = -h + 'px';
-					this.$refs.selection.style.top = cursorY + 'px';
+					selection.style.height = -h + 'px';
+					selection.style.top = cursorY + 'px';
 				}
 			};
 
@@ -368,23 +196,13 @@
 				document.documentElement.removeEventListener('mousemove', move);
 				document.documentElement.removeEventListener('mouseup', up);
 
-				this.$refs.selection.style.display = 'none';
+				selection.style.display = 'none';
 			};
 
 			document.documentElement.addEventListener('mousemove', move);
 			document.documentElement.addEventListener('mouseup', up);
-		};
-
-		this.pathOncontextmenu = e => {
-			e.preventDefault();
-			e.stopImmediatePropagation();
-			return false;
-		};
-
-		this.ondragover = e => {
-			e.preventDefault();
-			e.stopPropagation();
-
+		},
+		onDragover(e): any {
 			// ドラッグ元が自分自身の所有するアイテムかどうか
 			if (!this.isDragSource) {
 				// ドラッグされてきたものがファイルだったら
@@ -395,21 +213,14 @@
 				e.dataTransfer.dropEffect = 'none';
 				return false;
 			}
-		};
-
-		this.ondragenter = e => {
-			e.preventDefault();
+		},
+		onDragenter(e) {
 			if (!this.isDragSource) this.draghover = true;
-		};
-
-		this.ondragleave = e => {
+		},
+		onDragleave(e) {
 			this.draghover = false;
-		};
-
-		this.ondrop = e => {
-			e.preventDefault();
-			e.stopPropagation();
-
+		},
+		onDrop(e): any {
 			this.draghover = false;
 
 			// ドロップされてきたものがファイルだったら
@@ -433,7 +244,7 @@
 				const file = obj.id;
 				if (this.files.some(f => f.id == file)) return false;
 				this.removeFile(file);
-				this.api('drive/files/update', {
+				this.$root.$data.os.api('drive/files/update', {
 					file_id: file,
 					folder_id: this.folder ? this.folder.id : null
 				});
@@ -444,7 +255,7 @@
 				if (this.folder && folder == this.folder.id) return false;
 				if (this.folders.some(f => f.id == folder)) return false;
 				this.removeFolder(folder);
-				this.api('drive/folders/update', {
+				this.$root.$data.os.api('drive/folders/update', {
 					folder_id: folder,
 					parent_id: this.folder ? this.folder.id : null
 				}).then(() => {
@@ -464,32 +275,26 @@
 			}
 
 			return false;
-		};
-
-		this.oncontextmenu = e => {
-			e.preventDefault();
-			e.stopImmediatePropagation();
-
-			const ctx = riot.mount(document.body.appendChild(document.createElement('mk-drive-browser-base-contextmenu')), {
-				browser: this
-			})[0];
-			ctx.open({
-				x: e.pageX - window.pageXOffset,
-				y: e.pageY - window.pageYOffset
-			});
+		},
+		onContextmenu(e) {
+			document.body.appendChild(new MkDriveContextmenu({
+				propsData: {
+					browser: this,
+					x: e.pageX - window.pageXOffset,
+					y: e.pageY - window.pageYOffset
+				}
+			}).$mount().$el);
 
 			return false;
-		};
-
-		this.selectLocalFile = () => {
-			this.$refs.fileInput.click();
-		};
-
-		this.urlUpload = () => {
+		},
+		selectLocalFile() {
+			(this.$refs.fileInput as any).click();
+		},
+		urlUpload() {
 			inputDialog('%i18n:desktop.tags.mk-drive-browser.url-upload%',
 				'%i18n:desktop.tags.mk-drive-browser.url-of-file%', null, url => {
 
-				this.api('drive/files/upload_from_url', {
+				this.$root.$data.os.api('drive/files/upload_from_url', {
 					url: url,
 					folder_id: this.folder ? this.folder.id : undefined
 				});
@@ -499,34 +304,29 @@
 					text: '%i18n:common.ok%'
 				}]);
 			});
-		};
-
-		this.createFolder = () => {
+		},
+		createFolder() {
 			inputDialog('%i18n:desktop.tags.mk-drive-browser.create-folder%',
 				'%i18n:desktop.tags.mk-drive-browser.folder-name%', null, name => {
 
-				this.api('drive/folders/create', {
+				this.$root.$data.os.api('drive/folders/create', {
 					name: name,
 					folder_id: this.folder ? this.folder.id : undefined
 				}).then(folder => {
 					this.addFolder(folder, true);
-					this.update();
 				});
 			});
-		};
-
-		this.changeFileInput = () => {
-			Array.from(this.$refs.fileInput.files).forEach(file => {
+		},
+		onChangeFileInput() {
+			Array.from((this.$refs.fileInput as any).files).forEach(file => {
 				this.upload(file, this.folder);
 			});
-		};
-
-		this.upload = (file, folder) => {
+		},
+		upload(file, folder) {
 			if (folder && typeof folder == 'object') folder = folder.id;
-			this.$refs.uploader.upload(file, folder);
-		};
-
-		this.chooseFile = file => {
+			(this.$refs.uploader as any).upload(file, folder);
+		},
+		chooseFile(file) {
 			const isAlreadySelected = this.selectedFiles.some(f => f.id == file.id);
 			if (this.multiple) {
 				if (isAlreadySelected) {
@@ -534,7 +334,6 @@
 				} else {
 					this.selectedFiles.push(file);
 				}
-				this.update();
 				this.$emit('change-selection', this.selectedFiles);
 			} else {
 				if (isAlreadySelected) {
@@ -544,15 +343,15 @@
 					this.$emit('change-selection', [file]);
 				}
 			}
-		};
-
-		this.newWindow = folderId => {
-			riot.mount(document.body.appendChild(document.createElement('mk-drive-browser-window')), {
-				folder: folderId
-			});
-		};
-
-		this.move = target => {
+		},
+		newWindow(folderId) {
+			document.body.appendChild(new MkDriveWindow({
+				propsData: {
+					folder: folderId
+				}
+			}).$mount().$el);
+		},
+		move(target) {
 			if (target == null) {
 				this.goRoot();
 				return;
@@ -560,11 +359,9 @@
 				target = target.id;
 			}
 
-			this.update({
-				fetching: true
-			});
+			this.fetching = true;
 
-			this.api('drive/folders/show', {
+			this.$root.$data.os.api('drive/folders/show', {
 				folder_id: target
 			}).then(folder => {
 				this.folder = folder;
@@ -577,20 +374,17 @@
 
 				if (folder.parent) dive(folder.parent);
 
-				this.update();
 				this.$emit('open-folder', folder);
 				this.fetch();
 			});
-		};
-
-		this.addFolder = (folder, unshift = false) => {
+		},
+		addFolder(folder, unshift = false) {
 			const current = this.folder ? this.folder.id : null;
 			if (current != folder.parent_id) return;
 
 			if (this.folders.some(f => f.id == folder.id)) {
 				const exist = this.folders.map(f => f.id).indexOf(folder.id);
-				this.folders[exist] = folder;
-				this.update();
+				this.folders[exist] = folder; // TODO
 				return;
 			}
 
@@ -599,18 +393,14 @@
 			} else {
 				this.folders.push(folder);
 			}
-
-			this.update();
-		};
-
-		this.addFile = (file, unshift = false) => {
+		},
+		addFile(file, unshift = false) {
 			const current = this.folder ? this.folder.id : null;
 			if (current != file.folder_id) return;
 
 			if (this.files.some(f => f.id == file.id)) {
 				const exist = this.files.map(f => f.id).indexOf(file.id);
-				this.files[exist] = file;
-				this.update();
+				this.files[exist] = file; // TODO
 				return;
 			}
 
@@ -619,47 +409,42 @@
 			} else {
 				this.files.push(file);
 			}
-
-			this.update();
-		};
-
-		this.removeFolder = folder => {
+		},
+		removeFolder(folder) {
 			if (typeof folder == 'object') folder = folder.id;
 			this.folders = this.folders.filter(f => f.id != folder);
-			this.update();
-		};
-
-		this.removeFile = file => {
+		},
+		removeFile(file) {
 			if (typeof file == 'object') file = file.id;
 			this.files = this.files.filter(f => f.id != file);
-			this.update();
-		};
-
-		this.appendFile = file => this.addFile(file);
-		this.appendFolder = file => this.addFolder(file);
-		this.prependFile = file => this.addFile(file, true);
-		this.prependFolder = file => this.addFolder(file, true);
-
-		this.goRoot = () => {
+		},
+		appendFile(file) {
+			this.addFile(file);
+		},
+		appendFolder(folder) {
+			this.addFolder(folder);
+		},
+		prependFile(file) {
+			this.addFile(file, true);
+		},
+		prependFolder(folder) {
+			this.addFolder(folder, true);
+		},
+		goRoot() {
 			// 既にrootにいるなら何もしない
 			if (this.folder == null) return;
 
-			this.update({
-				folder: null,
-				hierarchyFolders: []
-			});
+			this.folder = null;
+			this.hierarchyFolders = [];
 			this.$emit('move-root');
 			this.fetch();
-		};
-
-		this.fetch = () => {
-			this.update({
-				folders: [],
-				files: [],
-				moreFolders: false,
-				moreFiles: false,
-				fetching: true
-			});
+		},
+		fetch() {
+			this.folders = [];
+			this.files = [];
+			this.moreFolders = false;
+			this.moreFiles = false;
+			this.fetching = true;
 
 			let fetchedFolders = null;
 			let fetchedFiles = null;
@@ -668,7 +453,7 @@
 			const filesMax = 30;
 
 			// フォルダ一覧取得
-			this.api('drive/folders', {
+			this.$root.$data.os.api('drive/folders', {
 				folder_id: this.folder ? this.folder.id : null,
 				limit: foldersMax + 1
 			}).then(folders => {
@@ -681,7 +466,7 @@
 			});
 
 			// ファイル一覧取得
-			this.api('drive/files', {
+			this.$root.$data.os.api('drive/files', {
 				folder_id: this.folder ? this.folder.id : null,
 				limit: filesMax + 1
 			}).then(files => {
@@ -698,24 +483,19 @@
 				if (flag) {
 					fetchedFolders.forEach(this.appendFolder);
 					fetchedFiles.forEach(this.appendFile);
-					this.update({
-						fetching: false
-					});
+					this.fetching = false;
 				} else {
 					flag = true;
 				}
 			};
-		};
-
-		this.fetchMoreFiles = () => {
-			this.update({
-				fetching: true
-			});
+		},
+		fetchMoreFiles() {
+			this.fetching = true;
 
 			const max = 30;
 
 			// ファイル一覧取得
-			this.api('drive/files', {
+			this.$root.$data.os.api('drive/files', {
 				folder_id: this.folder ? this.folder.id : null,
 				limit: max + 1
 			}).then(files => {
@@ -726,11 +506,205 @@
 					this.moreFiles = false;
 				}
 				files.forEach(this.appendFile);
-				this.update({
-					fetching: false
-				});
+				this.fetching = false;
 			});
-		};
+		}
+	}
+});
+</script>
 
-	</script>
-</mk-drive-browser>
+<style lang="stylus" scoped>
+.mk-drive
+
+	> nav
+		display block
+		z-index 2
+		width 100%
+		overflow auto
+		font-size 0.9em
+		color #555
+		background #fff
+		//border-bottom 1px solid #dfdfdf
+		box-shadow 0 1px 0 rgba(0, 0, 0, 0.05)
+
+		&, *
+			user-select none
+
+		> .path
+			display inline-block
+			vertical-align bottom
+			margin 0
+			padding 0 8px
+			width calc(100% - 200px)
+			line-height 38px
+			white-space nowrap
+
+			> *
+				display inline-block
+				margin 0
+				padding 0 8px
+				line-height 38px
+				cursor pointer
+
+				i
+					margin-right 4px
+
+				*
+					pointer-events none
+
+				&:hover
+					text-decoration underline
+
+				&.current
+					font-weight bold
+					cursor default
+
+					&:hover
+						text-decoration none
+
+				&.separator
+					margin 0
+					padding 0
+					opacity 0.5
+					cursor default
+
+					> [data-fa]
+						margin 0
+
+		> .search
+			display inline-block
+			vertical-align bottom
+			user-select text
+			cursor auto
+			margin 0
+			padding 0 18px
+			width 200px
+			font-size 1em
+			line-height 38px
+			background transparent
+			outline none
+			//border solid 1px #ddd
+			border none
+			border-radius 0
+			box-shadow none
+			transition color 0.5s ease, border 0.5s ease
+			font-family FontAwesome, sans-serif
+
+			&[data-active='true']
+				background #fff
+
+			&::-webkit-input-placeholder,
+			&:-ms-input-placeholder,
+			&:-moz-placeholder
+				color $ui-control-foreground-color
+
+	> .main
+		padding 8px
+		height calc(100% - 38px)
+		overflow auto
+
+		&, *
+			user-select none
+
+		&.fetching
+			cursor wait !important
+
+			*
+				pointer-events none
+
+			> .contents
+				opacity 0.5
+
+		&.uploading
+			height calc(100% - 38px - 100px)
+
+		> .selection
+			display none
+			position absolute
+			z-index 128
+			top 0
+			left 0
+			border solid 1px $theme-color
+			background rgba($theme-color, 0.5)
+			pointer-events none
+
+		> .contents
+
+			> .folders
+			> .files
+				display flex
+				flex-wrap wrap
+
+				> .folder
+				> .file
+					flex-grow 1
+					width 144px
+					margin 4px
+
+				> .padding
+					flex-grow 1
+					pointer-events none
+					width 144px + 8px // 8px is margin
+
+			> .empty
+				padding 16px
+				text-align center
+				color #999
+				pointer-events none
+
+				> p
+					margin 0
+
+		> .fetching
+			.spinner
+				margin 100px auto
+				width 40px
+				height 40px
+				text-align center
+
+				animation sk-rotate 2.0s infinite linear
+
+			.dot1, .dot2
+				width 60%
+				height 60%
+				display inline-block
+				position absolute
+				top 0
+				background-color rgba(0, 0, 0, 0.3)
+				border-radius 100%
+
+				animation sk-bounce 2.0s infinite ease-in-out
+
+			.dot2
+				top auto
+				bottom 0
+				animation-delay -1.0s
+
+			@keyframes sk-rotate { 100% { transform: rotate(360deg); }}
+
+			@keyframes sk-bounce {
+				0%, 100% {
+					transform: scale(0.0);
+				} 50% {
+					transform: scale(1.0);
+				}
+			}
+
+	> .dropzone
+		position absolute
+		left 0
+		top 38px
+		width 100%
+		height calc(100% - 38px)
+		border dashed 2px rgba($theme-color, 0.5)
+		pointer-events none
+
+	> .mk-uploader
+		height 100px
+		padding 16px
+		background #fff
+
+	> input
+		display none
+
+</style>