This commit is contained in:
syuilo 2018-02-14 19:03:48 +09:00
parent 727d2d8737
commit 9a722898d2
6 changed files with 457 additions and 422 deletions

View file

@ -1,217 +0,0 @@
<mk-drive-browser-file data-is-selected={ isSelected } data-is-contextmenu-showing={ isContextmenuShowing.toString() } @click="onclick" oncontextmenu={ oncontextmenu } draggable="true" ondragstart={ ondragstart } ondragend={ ondragend } title={ title }>
<div class="label" v-if="I.avatar_id == file.id"><img src="/assets/label.svg"/>
<p>%i18n:desktop.tags.mk-drive-browser-file.avatar%</p>
</div>
<div class="label" v-if="I.banner_id == file.id"><img src="/assets/label.svg"/>
<p>%i18n:desktop.tags.mk-drive-browser-file.banner%</p>
</div>
<div class="thumbnail" ref="thumbnail" style="background-color:{ file.properties.average_color ? 'rgb(' + file.properties.average_color.join(',') + ')' : 'transparent' }">
<img src={ file.url + '?thumbnail&size=128' } alt="" onload={ onload }/>
</div>
<p class="name"><span>{ file.name.lastIndexOf('.') != -1 ? file.name.substr(0, file.name.lastIndexOf('.')) : file.name }</span><span class="ext" v-if="file.name.lastIndexOf('.') != -1">{ file.name.substr(file.name.lastIndexOf('.')) }</span></p>
<style lang="stylus" scoped>
:scope
display block
padding 8px 0 0 0
height 180px
border-radius 4px
&, *
cursor pointer
&:hover
background rgba(0, 0, 0, 0.05)
> .label
&:before
&:after
background #0b65a5
&:active
background rgba(0, 0, 0, 0.1)
> .label
&:before
&:after
background #0b588c
&[data-is-selected]
background $theme-color
&:hover
background lighten($theme-color, 10%)
&:active
background darken($theme-color, 10%)
> .label
&:before
&:after
display none
> .name
color $theme-color-foreground
&[data-is-contextmenu-showing='true']
&:after
content ""
pointer-events none
position absolute
top -4px
right -4px
bottom -4px
left -4px
border 2px dashed rgba($theme-color, 0.3)
border-radius 4px
> .label
position absolute
top 0
left 0
pointer-events none
&:before
content ""
display block
position absolute
z-index 1
top 0
left 57px
width 28px
height 8px
background #0c7ac9
&:after
content ""
display block
position absolute
z-index 1
top 57px
left 0
width 8px
height 28px
background #0c7ac9
> img
position absolute
z-index 2
top 0
left 0
> p
position absolute
z-index 3
top 19px
left -28px
width 120px
margin 0
text-align center
line-height 28px
color #fff
transform rotate(-45deg)
> .thumbnail
width 128px
height 128px
margin auto
> img
display block
position absolute
top 0
left 0
right 0
bottom 0
margin auto
max-width 128px
max-height 128px
pointer-events none
> .name
display block
margin 4px 0 0 0
font-size 0.8em
text-align center
word-break break-all
color #444
overflow hidden
> .ext
opacity 0.5
</style>
<script lang="typescript">
import * as anime from 'animejs';
import bytesToSize from '../../../common/scripts/bytes-to-size';
this.mixin('i');
this.file = this.opts.file;
this.browser = this.parent;
this.title = `${this.file.name}\n${this.file.type} ${bytesToSize(this.file.datasize)}`;
this.isContextmenuShowing = false;
this.isSelected = this.browser.selectedFiles.some(f => f.id == this.file.id);
this.browser.on('change-selection', selections => {
this.isSelected = selections.some(f => f.id == this.file.id);
this.update();
});
this.onclick = () => {
this.browser.chooseFile(this.file);
};
this.oncontextmenu = e => {
e.preventDefault();
e.stopImmediatePropagation();
this.update({
isContextmenuShowing: true
});
const ctx = riot.mount(document.body.appendChild(document.createElement('mk-drive-browser-file-contextmenu')), {
browser: this.browser,
file: this.file
})[0];
ctx.open({
x: e.pageX - window.pageXOffset,
y: e.pageY - window.pageYOffset
});
ctx.on('closed', () => {
this.update({
isContextmenuShowing: false
});
});
return false;
};
this.ondragstart = e => {
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('text', JSON.stringify({
type: 'file',
id: this.file.id,
file: this.file
}));
this.isDragging = true;
// 親ブラウザに対して、ドラッグが開始されたフラグを立てる
// (=あなたの子供が、ドラッグを開始しましたよ)
this.browser.isDragSource = true;
};
this.ondragend = e => {
this.isDragging = false;
this.browser.isDragSource = false;
};
this.onload = () => {
if (this.file.properties.average_color) {
anime({
targets: this.$refs.thumbnail,
backgroundColor: `rgba(${this.file.properties.average_color.join(',')}, 0)`,
duration: 100,
easing: 'linear'
});
}
};
</script>
</mk-drive-browser-file>

View file

@ -1,202 +0,0 @@
<mk-drive-browser-folder data-is-contextmenu-showing={ isContextmenuShowing.toString() } data-draghover={ draghover.toString() } @click="onclick" onmouseover={ onmouseover } onmouseout={ onmouseout } ondragover={ ondragover } ondragenter={ ondragenter } ondragleave={ ondragleave } ondrop={ ondrop } oncontextmenu={ oncontextmenu } draggable="true" ondragstart={ ondragstart } ondragend={ ondragend } title={ title }>
<p class="name"><template v-if="hover">%fa:R folder-open .fw%</template><template v-if="!hover">%fa:R folder .fw%</template>{ folder.name }</p>
<style lang="stylus" scoped>
:scope
display block
padding 8px
height 64px
background lighten($theme-color, 95%)
border-radius 4px
&, *
cursor pointer
*
pointer-events none
&:hover
background lighten($theme-color, 90%)
&:active
background lighten($theme-color, 85%)
&[data-is-contextmenu-showing='true']
&[data-draghover='true']
&:after
content ""
pointer-events none
position absolute
top -4px
right -4px
bottom -4px
left -4px
border 2px dashed rgba($theme-color, 0.3)
border-radius 4px
&[data-draghover='true']
background lighten($theme-color, 90%)
> .name
margin 0
font-size 0.9em
color darken($theme-color, 30%)
> [data-fa]
margin-right 4px
margin-left 2px
text-align left
</style>
<script lang="typescript">
import dialog from '../../scripts/dialog';
this.mixin('api');
this.folder = this.opts.folder;
this.browser = this.parent;
this.title = this.folder.name;
this.hover = false;
this.draghover = false;
this.isContextmenuShowing = false;
this.onclick = () => {
this.browser.move(this.folder);
};
this.onmouseover = () => {
this.hover = true;
};
this.onmouseout = () => {
this.hover = false
};
this.ondragover = e => {
e.preventDefault();
e.stopPropagation();
// 自分自身がドラッグされていない場合
if (!this.isDragging) {
// ドラッグされてきたものがファイルだったら
if (e.dataTransfer.effectAllowed === 'all') {
e.dataTransfer.dropEffect = 'copy';
} else {
e.dataTransfer.dropEffect = 'move';
}
} else {
// 自分自身にはドロップさせない
e.dataTransfer.dropEffect = 'none';
}
return false;
};
this.ondragenter = e => {
e.preventDefault();
if (!this.isDragging) this.draghover = true;
};
this.ondragleave = () => {
this.draghover = false;
};
this.ondrop = e => {
e.preventDefault();
e.stopPropagation();
this.draghover = false;
// ファイルだったら
if (e.dataTransfer.files.length > 0) {
Array.from(e.dataTransfer.files).forEach(file => {
this.browser.upload(file, this.folder);
});
return false;
};
// データ取得
const data = e.dataTransfer.getData('text');
if (data == null) return false;
// パース
// TODO: Validate JSON
const obj = JSON.parse(data);
// (ドライブの)ファイルだったら
if (obj.type == 'file') {
const file = obj.id;
this.browser.removeFile(file);
this.api('drive/files/update', {
file_id: file,
folder_id: this.folder.id
});
// (ドライブの)フォルダーだったら
} else if (obj.type == 'folder') {
const folder = obj.id;
// 移動先が自分自身ならreject
if (folder == this.folder.id) return false;
this.browser.removeFolder(folder);
this.api('drive/folders/update', {
folder_id: folder,
parent_id: this.folder.id
}).then(() => {
// something
}).catch(err => {
switch (err) {
case 'detected-circular-definition':
dialog('%fa:exclamation-triangle%%i18n:desktop.tags.mk-drive-browser-folder.unable-to-process%',
'%i18n:desktop.tags.mk-drive-browser-folder.circular-reference-detected%', [{
text: '%i18n:common.ok%'
}]);
break;
default:
alert('%i18n:desktop.tags.mk-drive-browser-folder.unhandled-error% ' + err);
}
});
}
return false;
};
this.ondragstart = e => {
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('text', JSON.stringify({
type: 'folder',
id: this.folder.id
}));
this.isDragging = true;
// 親ブラウザに対して、ドラッグが開始されたフラグを立てる
// (=あなたの子供が、ドラッグを開始しましたよ)
this.browser.isDragSource = true;
};
this.ondragend = e => {
this.isDragging = false;
this.browser.isDragSource = false;
};
this.oncontextmenu = e => {
e.preventDefault();
e.stopImmediatePropagation();
this.update({
isContextmenuShowing: true
});
const ctx = riot.mount(document.body.appendChild(document.createElement('mk-drive-browser-folder-contextmenu')), {
browser: this.browser,
folder: this.folder
})[0];
ctx.open({
x: e.pageX - window.pageXOffset,
y: e.pageY - window.pageYOffset
});
ctx.on('closed', () => {
this.update({
isContextmenuShowing: false
});
});
return false;
};
</script>
</mk-drive-browser-folder>

View file

@ -0,0 +1,232 @@
<template>
<div class="mk-drive-file"
:data-is-selected="isSelected"
:data-is-contextmenu-showing="isContextmenuShowing"
@click="onClick"
@contextmenu.prevent.stop="onContextmenu"
draggable="true"
@dragstart="onDragstart"
@dragend="onDragend"
:title="title"
>
<div class="label" v-if="I.avatar_id == file.id"><img src="/assets/label.svg"/>
<p>%i18n:desktop.tags.mk-drive-browser-file.avatar%</p>
</div>
<div class="label" v-if="I.banner_id == file.id"><img src="/assets/label.svg"/>
<p>%i18n:desktop.tags.mk-drive-browser-file.banner%</p>
</div>
<div class="thumbnail" ref="thumbnail" style="background-color:{ file.properties.average_color ? 'rgb(' + file.properties.average_color.join(',') + ')' : 'transparent' }">
<img src={ file.url + '?thumbnail&size=128' } alt="" @load="onThumbnailLoaded"/>
</div>
<p class="name">
<span>{ file.name.lastIndexOf('.') != -1 ? file.name.substr(0, file.name.lastIndexOf('.')) : file.name }</span>
<span class="ext" v-if="file.name.lastIndexOf('.') != -1">{ file.name.substr(file.name.lastIndexOf('.')) }</span>
</p>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import * as anime from 'animejs';
import bytesToSize from '../../../common/scripts/bytes-to-size';
export default Vue.extend({
props: ['file', 'browser'],
data() {
return {
isContextmenuShowing: false,
isDragging: false
};
},
computed: {
isSelected(): boolean {
return this.browser.selectedFiles.some(f => f.id == this.file.id);
},
title(): string {
return `${this.file.name}\n${this.file.type} ${bytesToSize(this.file.datasize)}`;
}
},
methods: {
onClick() {
this.browser.chooseFile(this.file);
},
onContextmenu(e) {
this.isContextmenuShowing = true;
const ctx = new MkDriveFileContextmenu({
parent: this,
propsData: {
browser: this.browser,
x: e.pageX - window.pageXOffset,
y: e.pageY - window.pageYOffset
}
}).$mount();
ctx.$once('closed', () => {
this.isContextmenuShowing = false;
});
document.body.appendChild(ctx.$el);
},
onDragstart(e) {
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('text', JSON.stringify({
type: 'file',
id: this.file.id,
file: this.file
}));
this.isDragging = true;
//
// (=)
this.browser.isDragSource = true;
},
onDragend(e) {
this.isDragging = false;
this.browser.isDragSource = false;
},
onThumbnailLoaded() {
if (this.file.properties.average_color) {
anime({
targets: this.$refs.thumbnail,
backgroundColor: `rgba(${this.file.properties.average_color.join(',')}, 0)`,
duration: 100,
easing: 'linear'
});
}
}
}
});
</script>
<style lang="stylus" scoped>
.mk-drive-file
padding 8px 0 0 0
height 180px
border-radius 4px
&, *
cursor pointer
&:hover
background rgba(0, 0, 0, 0.05)
> .label
&:before
&:after
background #0b65a5
&:active
background rgba(0, 0, 0, 0.1)
> .label
&:before
&:after
background #0b588c
&[data-is-selected]
background $theme-color
&:hover
background lighten($theme-color, 10%)
&:active
background darken($theme-color, 10%)
> .label
&:before
&:after
display none
> .name
color $theme-color-foreground
&[data-is-contextmenu-showing]
&:after
content ""
pointer-events none
position absolute
top -4px
right -4px
bottom -4px
left -4px
border 2px dashed rgba($theme-color, 0.3)
border-radius 4px
> .label
position absolute
top 0
left 0
pointer-events none
&:before
content ""
display block
position absolute
z-index 1
top 0
left 57px
width 28px
height 8px
background #0c7ac9
&:after
content ""
display block
position absolute
z-index 1
top 57px
left 0
width 8px
height 28px
background #0c7ac9
> img
position absolute
z-index 2
top 0
left 0
> p
position absolute
z-index 3
top 19px
left -28px
width 120px
margin 0
text-align center
line-height 28px
color #fff
transform rotate(-45deg)
> .thumbnail
width 128px
height 128px
margin auto
> img
display block
position absolute
top 0
left 0
right 0
bottom 0
margin auto
max-width 128px
max-height 128px
pointer-events none
> .name
display block
margin 4px 0 0 0
font-size 0.8em
text-align center
word-break break-all
color #444
overflow hidden
> .ext
opacity 0.5
</style>

View file

@ -0,0 +1,220 @@
<template>
<div class="mk-drive-folder"
:data-is-contextmenu-showing="isContextmenuShowing"
:data-draghover="draghover"
@click="onClick"
@mouseover="onMouseover"
@mouseout="onMouseout"
@dragover.prevent.stop="onDragover"
@dragenter.prevent="onDragenter"
@dragleave="onDragleave"
@drop.prevent.stop="onDrop"
@contextmenu.prevent.stop="onContextmenu"
draggable="true"
@dragstart="onDragstart"
@dragend="onDragend"
:title="title"
>
<p class="name">
<template v-if="hover">%fa:R folder-open .fw%</template>
<template v-if="!hover">%fa:R folder .fw%</template>
{{ folder.name }}
</p>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import dialog from '../../scripts/dialog';
export default Vue.extend({
props: ['folder', 'browser'],
data() {
return {
hover: false,
draghover: false,
isDragging: false,
isContextmenuShowing: false
};
},
computed: {
title(): string {
return this.folder.name;
}
},
methods: {
onClick() {
this.browser.move(this.folder);
},
onMouseover() {
this.hover = true;
},
onMouseout() {
this.hover = false
},
onDragover(e) {
//
if (!this.isDragging) {
//
if (e.dataTransfer.effectAllowed === 'all') {
e.dataTransfer.dropEffect = 'copy';
} else {
e.dataTransfer.dropEffect = 'move';
}
} else {
//
e.dataTransfer.dropEffect = 'none';
}
return false;
},
onDragenter() {
if (!this.isDragging) this.draghover = true;
},
onDragleave() {
this.draghover = false;
},
onDrop(e) {
this.draghover = false;
//
if (e.dataTransfer.files.length > 0) {
Array.from(e.dataTransfer.files).forEach(file => {
this.browser.upload(file, this.folder);
});
return false;
};
//
const data = e.dataTransfer.getData('text');
if (data == null) return false;
//
// TODO: Validate JSON
const obj = JSON.parse(data);
// ()
if (obj.type == 'file') {
const file = obj.id;
this.browser.removeFile(file);
this.$root.$data.os.api('drive/files/update', {
file_id: file,
folder_id: this.folder.id
});
// ()
} else if (obj.type == 'folder') {
const folder = obj.id;
// reject
if (folder == this.folder.id) return false;
this.browser.removeFolder(folder);
this.$root.$data.os.api('drive/folders/update', {
folder_id: folder,
parent_id: this.folder.id
}).then(() => {
// something
}).catch(err => {
switch (err) {
case 'detected-circular-definition':
dialog('%fa:exclamation-triangle%%i18n:desktop.tags.mk-drive-browser-folder.unable-to-process%',
'%i18n:desktop.tags.mk-drive-browser-folder.circular-reference-detected%', [{
text: '%i18n:common.ok%'
}]);
break;
default:
alert('%i18n:desktop.tags.mk-drive-browser-folder.unhandled-error% ' + err);
}
});
}
return false;
},
onDragstart(e) {
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('text', JSON.stringify({
type: 'folder',
id: this.folder.id
}));
this.isDragging = true;
//
// (=)
this.browser.isDragSource = true;
},
onDragend() {
this.isDragging = false;
this.browser.isDragSource = false;
},
onContextmenu(e) {
this.isContextmenuShowing = true;
const ctx = new MkDriveFolderContextmenu({
parent: this,
propsData: {
browser: this.browser,
x: e.pageX - window.pageXOffset,
y: e.pageY - window.pageYOffset
}
}).$mount();
ctx.$once('closed', () => {
this.isContextmenuShowing = false;
});
document.body.appendChild(ctx.$el);
return false;
}
}
});
</script>
<style lang="stylus" scoped>
.mk-drive-folder
padding 8px
height 64px
background lighten($theme-color, 95%)
border-radius 4px
&, *
cursor pointer
*
pointer-events none
&:hover
background lighten($theme-color, 90%)
&:active
background lighten($theme-color, 85%)
&[data-is-contextmenu-showing]
&[data-draghover]
&:after
content ""
pointer-events none
position absolute
top -4px
right -4px
bottom -4px
left -4px
border 2px dashed rgba($theme-color, 0.3)
border-radius 4px
&[data-draghover]
background lighten($theme-color, 90%)
> .name
margin 0
font-size 0.9em
color darken($theme-color, 30%)
> [data-fa]
margin-right 4px
margin-left 2px
text-align left
</style>

View file

@ -29,7 +29,9 @@ export default Vue.extend({
}; };
}, },
mounted() { mounted() {
(this.$refs.form as any).focus(); Vue.nextTick(() => {
(this.$refs.form as any).focus();
});
}, },
methods: { methods: {
onChangeUploadings(media) { onChangeUploadings(media) {

View file

@ -111,7 +111,7 @@ export default Vue.extend({
chooseFile() { chooseFile() {
(this.$refs.file as any).click(); (this.$refs.file as any).click();
}, },
chooseFileFromDrive() { chooseFileFromDrive() {/*
const w = new MkDriveFileSelectorWindow({ const w = new MkDriveFileSelectorWindow({
propsData: { propsData: {
multiple: true multiple: true
@ -122,7 +122,7 @@ export default Vue.extend({
w.$once('selected', files => { w.$once('selected', files => {
files.forEach(this.attachMedia); files.forEach(this.attachMedia);
}); });*/
}, },
attachMedia(driveFile) { attachMedia(driveFile) {
this.files.push(driveFile); this.files.push(driveFile);