hippofish/packages/client/src/components/MkDrive.vue

922 lines
20 KiB
Vue
Raw Normal View History

2018-02-14 07:54:18 +01:00
<template>
2023-04-08 02:01:42 +02:00
<div class="yfudmmck">
<nav>
<div class="path" @contextmenu.prevent.stop="() => {}">
<XNavFolder
2023-04-08 02:01:42 +02:00
:class="{ current: folder == null }"
:parent-folder="folder"
@move="move"
@upload="upload"
@removeFile="removeFile"
@removeFolder="removeFolder"
/>
2023-04-08 02:01:42 +02:00
<template v-for="f in hierarchyFolders">
<span class="separator"
><i :class="icon('ph-caret-right')"></i
2023-04-08 02:01:42 +02:00
></span>
<XNavFolder
:folder="f"
:parent-folder="folder"
@move="move"
@upload="upload"
@removeFile="removeFile"
@removeFolder="removeFolder"
/>
</template>
<span v-if="folder != null" class="separator"
><i :class="icon('ph-caret-right')"></i
2023-04-08 02:01:42 +02:00
></span>
<span v-if="folder != null" class="folder current">{{
folder.name
}}</span>
2017-01-11 21:55:38 +01:00
</div>
2023-04-08 02:01:42 +02:00
<button class="menu _button" @click="showMenu">
<i :class="icon('ph-dots-three-outline')"></i>
2023-04-08 02:01:42 +02:00
</button>
</nav>
<div
ref="main"
class="main"
:class="{ uploading: uploadings.length > 0, fetching }"
@dragover.prevent.stop="onDragover"
@dragenter="onDragenter"
@dragleave="onDragleave"
@drop.prevent.stop="onDrop"
@contextmenu.stop="onContextmenu"
>
<div ref="contents" class="contents">
<div
v-show="folders.length > 0"
ref="foldersContainer"
class="folders"
>
<XFolder
v-for="(f, i) in folders"
:key="f.id"
v-anim="i"
class="folder"
:folder="f"
:select-mode="select === 'folder'"
:is-selected="
selectedFolders.some((x) => x.id === f.id)
"
@chosen="chooseFolder"
@move="move"
@upload="upload"
@removeFile="removeFile"
@removeFolder="removeFolder"
@dragstart="isDragSource = true"
@dragend="isDragSource = false"
/>
<!-- SEE: https://stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid -->
2023-08-10 22:25:21 +02:00
<div v-for="(_, i) in 16" :key="i" class="padding"></div>
2023-04-08 02:01:42 +02:00
<MkButton v-if="moreFolders" ref="moreFolders">{{
i18n.ts.loadMore
}}</MkButton>
</div>
<div
v-show="files.length > 0"
ref="filesContainer"
class="files"
>
<XFile
v-for="(file, i) in files"
:key="file.id"
v-anim="i"
class="file"
:file="file"
:select-mode="select === 'file'"
:is-selected="
selectedFiles.some((x) => x.id === file.id)
"
@chosen="chooseFile"
@dragstart="isDragSource = true"
@dragend="isDragSource = false"
/>
<!-- SEE: https://stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid -->
2023-08-10 22:25:21 +02:00
<div v-for="(_, i) in 16" :key="i" class="padding"></div>
2023-04-08 02:01:42 +02:00
<MkButton
v-show="moreFiles"
ref="loadMoreFiles"
@click="fetchMoreFiles"
>{{ i18n.ts.loadMore }}</MkButton
>
</div>
<div
v-if="files.length == 0 && folders.length == 0 && !fetching"
class="empty"
>
<p v-if="draghover">{{ i18n.t("empty-draghover") }}</p>
<p v-if="!draghover && folder == null">
<strong>{{ i18n.ts.emptyDrive }}</strong
><br />{{ i18n.t("empty-drive-description") }}
</p>
<p v-if="!draghover && folder != null">
{{ i18n.ts.emptyFolder }}
</p>
</div>
2016-12-28 23:49:51 +01:00
</div>
2023-04-08 02:01:42 +02:00
<MkLoading v-if="fetching" />
2017-01-11 21:55:38 +01:00
</div>
2023-04-08 02:01:42 +02:00
<div v-if="draghover" class="dropzone"></div>
<input
ref="fileInput"
type="file"
accept="*/*"
multiple
tabindex="-1"
@change="onChangeFileInput"
/>
2017-01-11 21:55:38 +01:00
</div>
2018-02-14 07:54:18 +01:00
</template>
<script lang="ts" setup>
2023-04-08 02:01:42 +02:00
import {
nextTick,
onActivated,
onBeforeUnmount,
onMounted,
ref,
watch,
} from "vue";
2023-09-24 06:27:16 +02:00
import type * as firefish from "firefish-js";
2023-04-08 02:01:42 +02:00
import MkButton from "./MkButton.vue";
import XNavFolder from "@/components/MkDrive.navFolder.vue";
import XFolder from "@/components/MkDrive.folder.vue";
import XFile from "@/components/MkDrive.file.vue";
import * as os from "@/os";
import { stream } from "@/stream";
import { defaultStore } from "@/store";
import { i18n } from "@/i18n";
import { uploadFile, uploads } from "@/scripts/upload";
import icon from "@/scripts/icon";
2023-04-08 02:01:42 +02:00
const props = withDefaults(
defineProps<{
2023-09-24 06:27:16 +02:00
initialFolder?: firefish.entities.DriveFolder;
2023-04-08 02:01:42 +02:00
type?: string;
multiple?: boolean;
select?: "file" | "folder" | null;
}>(),
{
multiple: false,
select: null,
2023-07-06 03:28:27 +02:00
},
2023-04-08 02:01:42 +02:00
);
2018-02-14 07:54:18 +01:00
const emit = defineEmits<{
2023-04-08 02:01:42 +02:00
(
ev: "selected",
2023-09-24 06:27:16 +02:00
v: firefish.entities.DriveFile | firefish.entities.DriveFolder,
2023-04-08 02:01:42 +02:00
): void;
(
ev: "change-selection",
2023-09-24 06:27:16 +02:00
v: firefish.entities.DriveFile[] | firefish.entities.DriveFolder[],
2023-04-08 02:01:42 +02:00
): void;
(ev: "move-root"): void;
2023-09-24 06:27:16 +02:00
(ev: "cd", v: firefish.entities.DriveFolder | null): void;
(ev: "open-folder", v: firefish.entities.DriveFolder): void;
}>();
const loadMoreFiles = ref<InstanceType<typeof MkButton>>();
const fileInput = ref<HTMLInputElement>();
2023-09-24 06:27:16 +02:00
const folder = ref<firefish.entities.DriveFolder | null>(null);
const files = ref<firefish.entities.DriveFile[]>([]);
const folders = ref<firefish.entities.DriveFolder[]>([]);
const moreFiles = ref(false);
const moreFolders = ref(false);
2023-09-24 06:27:16 +02:00
const hierarchyFolders = ref<firefish.entities.DriveFolder[]>([]);
const selectedFiles = ref<firefish.entities.DriveFile[]>([]);
const selectedFolders = ref<firefish.entities.DriveFolder[]>([]);
const uploadings = uploads;
2023-04-08 02:01:42 +02:00
const connection = stream.useChannel("drive");
const keepOriginal = ref<boolean>(defaultStore.state.keepOriginalUploading); // 外部渡しが多いので$refは使わないほうがよい
// ドロップされようとしているか
const draghover = ref(false);
// 自身の所有するアイテムがドラッグをスタートさせたか
// (自分自身の階層にドロップできないようにするためのフラグ)
const isDragSource = ref(false);
const fetching = ref(true);
const ilFilesObserver = new IntersectionObserver(
2023-04-08 02:01:42 +02:00
(entries) =>
entries.some((entry) => entry.isIntersecting) &&
!fetching.value &&
moreFiles.value &&
2023-07-06 03:28:27 +02:00
fetchMoreFiles(),
);
2023-04-08 02:01:42 +02:00
watch(folder, () => emit("cd", folder.value));
2023-09-24 06:27:16 +02:00
function onStreamDriveFileCreated(file: firefish.entities.DriveFile) {
addFile(file, true);
}
2023-09-24 06:27:16 +02:00
function onStreamDriveFileUpdated(file: firefish.entities.DriveFile) {
const current = folder.value ? folder.value.id : null;
if (current !== file.folderId) {
removeFile(file);
} else {
addFile(file, true);
}
}
function onStreamDriveFileDeleted(fileId: string) {
removeFile(fileId);
}
2023-04-08 02:01:42 +02:00
function onStreamDriveFolderCreated(
2023-09-24 06:27:16 +02:00
createdFolder: firefish.entities.DriveFolder,
2023-04-08 02:01:42 +02:00
) {
addFolder(createdFolder, true);
}
2023-04-08 02:01:42 +02:00
function onStreamDriveFolderUpdated(
2023-09-24 06:27:16 +02:00
updatedFolder: firefish.entities.DriveFolder,
2023-04-08 02:01:42 +02:00
) {
const current = folder.value ? folder.value.id : null;
if (current !== updatedFolder.parentId) {
removeFolder(updatedFolder);
} else {
addFolder(updatedFolder, true);
}
}
function onStreamDriveFolderDeleted(folderId: string) {
removeFolder(folderId);
}
function onDragover(ev: DragEvent): any {
if (!ev.dataTransfer) return;
// ドラッグ元が自分自身の所有するアイテムだったら
if (isDragSource.value) {
// 自分自身にはドロップさせない
2023-04-08 02:01:42 +02:00
ev.dataTransfer.dropEffect = "none";
return;
}
2023-04-08 02:01:42 +02:00
const isFile = ev.dataTransfer.items[0].kind === "file";
const isDriveFile = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_;
2023-04-08 02:01:42 +02:00
const isDriveFolder =
ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FOLDER_;
if (isFile || isDriveFile || isDriveFolder) {
2023-04-08 02:01:42 +02:00
ev.dataTransfer.dropEffect =
ev.dataTransfer.effectAllowed === "all" ? "copy" : "move";
} else {
2023-04-08 02:01:42 +02:00
ev.dataTransfer.dropEffect = "none";
}
2018-02-14 07:54:18 +01:00
return false;
}
2018-02-14 07:54:18 +01:00
function onDragenter() {
if (!isDragSource.value) draghover.value = true;
}
function onDragleave() {
draghover.value = false;
}
function onDrop(ev: DragEvent): any {
draghover.value = false;
if (!ev.dataTransfer) return;
// ドロップされてきたものがファイルだったら
if (ev.dataTransfer.files.length > 0) {
for (const file of Array.from(ev.dataTransfer.files)) {
upload(file, folder.value);
}
return;
}
2023-09-02 01:27:33 +02:00
// #region ドライブのファイル
const driveFile = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
2023-04-08 02:01:42 +02:00
if (driveFile != null && driveFile !== "") {
const file = JSON.parse(driveFile);
2023-04-08 02:01:42 +02:00
if (files.value.some((f) => f.id === file.id)) return;
removeFile(file.id);
2023-04-08 02:01:42 +02:00
os.api("drive/files/update", {
fileId: file.id,
2022-07-15 12:15:23 +02:00
folderId: folder.value ? folder.value.id : null,
});
}
2023-09-02 01:27:33 +02:00
// #endregion
2023-09-02 01:27:33 +02:00
// #region ドライブのフォルダ
const driveFolder = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FOLDER_);
2023-04-08 02:01:42 +02:00
if (driveFolder != null && driveFolder !== "") {
const droppedFolder = JSON.parse(driveFolder);
// 移動先が自分自身ならreject
if (folder.value && droppedFolder.id === folder.value.id) return false;
2023-04-08 02:01:42 +02:00
if (folders.value.some((f) => f.id === droppedFolder.id)) return false;
removeFolder(droppedFolder.id);
2023-04-08 02:01:42 +02:00
os.api("drive/folders/update", {
folderId: droppedFolder.id,
2022-07-15 12:15:23 +02:00
parentId: folder.value ? folder.value.id : null,
2023-04-08 02:01:42 +02:00
})
.then(() => {
// noop
})
.catch((err) => {
switch (err) {
case "detected-circular-definition":
os.alert({
title: i18n.ts.unableToProcess,
text: i18n.ts.circularReferenceFolder,
});
break;
default:
os.alert({
type: "error",
text: i18n.ts.somethingHappened,
});
}
});
}
2023-09-02 01:27:33 +02:00
// #endregion
}
function selectLocalFile() {
fileInput.value?.click();
}
function urlUpload() {
os.inputText({
title: i18n.ts.uploadFromUrl,
2023-04-08 02:01:42 +02:00
type: "url",
2022-07-15 12:15:23 +02:00
placeholder: i18n.ts.uploadFromUrlDescription,
}).then(({ canceled, result: url }) => {
if (canceled || !url) return;
2023-04-08 02:01:42 +02:00
os.api("drive/files/upload-from-url", {
2023-09-02 01:27:33 +02:00
url,
2022-07-15 12:15:23 +02:00
folderId: folder.value ? folder.value.id : undefined,
});
os.alert({
title: i18n.ts.uploadFromUrlRequested,
2022-07-15 12:15:23 +02:00
text: i18n.ts.uploadFromUrlMayTakeTime,
});
});
}
function createFolder() {
os.inputText({
title: i18n.ts.createFolder,
2022-07-15 12:15:23 +02:00
placeholder: i18n.ts.folderName,
}).then(({ canceled, result: name }) => {
if (canceled) return;
2023-04-08 02:01:42 +02:00
os.api("drive/folders/create", {
2023-09-02 01:27:33 +02:00
name,
2022-07-15 12:15:23 +02:00
parentId: folder.value ? folder.value.id : undefined,
2023-04-08 02:01:42 +02:00
}).then((createdFolder) => {
addFolder(createdFolder, true);
});
});
}
2023-09-24 06:27:16 +02:00
function renameFolder(folderToRename: firefish.entities.DriveFolder) {
os.inputText({
title: i18n.ts.renameFolder,
placeholder: i18n.ts.inputNewFolderName,
2022-07-15 12:15:23 +02:00
default: folderToRename.name,
}).then(({ canceled, result: name }) => {
if (canceled) return;
2023-04-08 02:01:42 +02:00
os.api("drive/folders/update", {
folderId: folderToRename.id,
2023-09-02 01:27:33 +02:00
name,
2023-04-08 02:01:42 +02:00
}).then((updatedFolder) => {
// FIXME: 画面を更新するために自分自身に移動
move(updatedFolder);
});
});
}
2023-09-24 06:27:16 +02:00
function deleteFolder(folderToDelete: firefish.entities.DriveFolder) {
2023-04-08 02:01:42 +02:00
os.api("drive/folders/delete", {
2022-07-15 12:15:23 +02:00
folderId: folderToDelete.id,
2023-04-08 02:01:42 +02:00
})
.then(() => {
// 削除時に親フォルダに移動
move(folderToDelete.parentId);
})
.catch((err) => {
switch (err.id) {
case "b0fc8a17-963c-405d-bfbc-859a487295e1":
os.alert({
type: "error",
title: i18n.ts.unableToDelete,
text: i18n.ts.hasChildFilesOrFolders,
});
break;
default:
os.alert({
type: "error",
text: i18n.ts.unableToDelete,
});
}
});
}
2018-02-26 22:25:17 +01:00
function onChangeFileInput() {
if (!fileInput.value?.files) return;
for (const file of Array.from(fileInput.value.files)) {
upload(file, folder.value);
}
}
2023-04-08 02:01:42 +02:00
function upload(
file: File,
2023-09-24 06:27:16 +02:00
folderToUpload?: firefish.entities.DriveFolder | null,
2023-04-08 02:01:42 +02:00
) {
uploadFile(
file,
folderToUpload && typeof folderToUpload === "object"
? folderToUpload.id
: null,
undefined,
2023-07-06 03:28:27 +02:00
keepOriginal.value,
2023-04-08 02:01:42 +02:00
).then((res) => {
addFile(res, true);
});
}
2023-09-24 06:27:16 +02:00
function chooseFile(file: firefish.entities.DriveFile) {
2023-04-08 02:01:42 +02:00
const isAlreadySelected = selectedFiles.value.some((f) => f.id === file.id);
if (props.multiple) {
if (isAlreadySelected) {
2023-04-08 02:01:42 +02:00
selectedFiles.value = selectedFiles.value.filter(
2023-07-06 03:28:27 +02:00
(f) => f.id !== file.id,
2023-04-08 02:01:42 +02:00
);
} else {
selectedFiles.value.push(file);
}
2023-04-08 02:01:42 +02:00
emit("change-selection", selectedFiles.value);
} else {
if (isAlreadySelected) {
2023-04-08 02:01:42 +02:00
emit("selected", file);
} else {
selectedFiles.value = [file];
2023-04-08 02:01:42 +02:00
emit("change-selection", [file]);
}
}
}
2018-02-26 22:25:17 +01:00
2023-09-24 06:27:16 +02:00
function chooseFolder(folderToChoose: firefish.entities.DriveFolder) {
2023-04-08 02:01:42 +02:00
const isAlreadySelected = selectedFolders.value.some(
2023-07-06 03:28:27 +02:00
(f) => f.id === folderToChoose.id,
2023-04-08 02:01:42 +02:00
);
if (props.multiple) {
if (isAlreadySelected) {
2023-04-08 02:01:42 +02:00
selectedFolders.value = selectedFolders.value.filter(
2023-07-06 03:28:27 +02:00
(f) => f.id !== folderToChoose.id,
2023-04-08 02:01:42 +02:00
);
} else {
selectedFolders.value.push(folderToChoose);
}
2023-04-08 02:01:42 +02:00
emit("change-selection", selectedFolders.value);
} else {
if (isAlreadySelected) {
2023-04-08 02:01:42 +02:00
emit("selected", folderToChoose);
} else {
selectedFolders.value = [folderToChoose];
2023-04-08 02:01:42 +02:00
emit("change-selection", [folderToChoose]);
}
}
}
2023-09-24 06:27:16 +02:00
function move(target?: firefish.entities.DriveFolder) {
if (!target) {
goRoot();
return;
2023-04-08 02:01:42 +02:00
} else if (typeof target === "object") {
target = target.id;
}
2018-02-26 22:35:16 +01:00
fetching.value = true;
2018-02-18 04:35:18 +01:00
2023-04-08 02:01:42 +02:00
os.api("drive/folders/show", {
2022-07-15 12:15:23 +02:00
folderId: target,
2023-04-08 02:01:42 +02:00
}).then((folderToMove) => {
folder.value = folderToMove;
hierarchyFolders.value = [];
2018-02-18 04:35:18 +01:00
2023-04-08 02:01:42 +02:00
const dive = (folderToDive) => {
hierarchyFolders.value.unshift(folderToDive);
if (folderToDive.parent) dive(folderToDive.parent);
};
2018-02-18 04:35:18 +01:00
if (folderToMove.parent) dive(folderToMove.parent);
2017-01-11 21:55:38 +01:00
2023-04-08 02:01:42 +02:00
emit("open-folder", folderToMove);
fetch();
});
}
2017-01-11 21:55:38 +01:00
2023-09-24 06:27:16 +02:00
function addFolder(
folderToAdd: firefish.entities.DriveFolder,
unshift = false,
) {
const current = folder.value ? folder.value.id : null;
if (current !== folderToAdd.parentId) return;
2017-02-16 21:46:14 +01:00
2023-04-08 02:01:42 +02:00
if (folders.value.some((f) => f.id === folderToAdd.id)) {
const exist = folders.value.map((f) => f.id).indexOf(folderToAdd.id);
folders.value[exist] = folderToAdd;
return;
}
if (unshift) {
folders.value.unshift(folderToAdd);
} else {
folders.value.push(folderToAdd);
}
}
2017-02-21 01:49:35 +01:00
2023-09-24 06:27:16 +02:00
function addFile(fileToAdd: firefish.entities.DriveFile, unshift = false) {
const current = folder.value ? folder.value.id : null;
if (current !== fileToAdd.folderId) return;
2016-12-28 23:49:51 +01:00
2023-04-08 02:01:42 +02:00
if (files.value.some((f) => f.id === fileToAdd.id)) {
const exist = files.value.map((f) => f.id).indexOf(fileToAdd.id);
files.value[exist] = fileToAdd;
return;
}
2016-12-28 23:49:51 +01:00
if (unshift) {
files.value.unshift(fileToAdd);
} else {
files.value.push(fileToAdd);
}
}
2023-09-24 06:27:16 +02:00
function removeFolder(folderToRemove: firefish.entities.DriveFolder | string) {
2023-04-08 02:01:42 +02:00
const folderIdToRemove =
typeof folderToRemove === "object" ? folderToRemove.id : folderToRemove;
folders.value = folders.value.filter((f) => f.id !== folderIdToRemove);
}
2023-09-24 06:27:16 +02:00
function removeFile(file: firefish.entities.DriveFile | string) {
2023-04-08 02:01:42 +02:00
const fileId = typeof file === "object" ? file.id : file;
files.value = files.value.filter((f) => f.id !== fileId);
}
2023-09-24 06:27:16 +02:00
function appendFile(file: firefish.entities.DriveFile) {
addFile(file);
}
2023-09-24 06:27:16 +02:00
function appendFolder(folderToAppend: firefish.entities.DriveFolder) {
addFolder(folderToAppend);
}
/*
2023-09-24 06:27:16 +02:00
function prependFile(file: firefish.entities.DriveFile) {
addFile(file, true);
}
2023-09-24 06:27:16 +02:00
function prependFolder(folderToPrepend: firefish.entities.DriveFolder) {
addFolder(folderToPrepend, true);
}
*/
function goRoot() {
// 既にrootにいるなら何もしない
if (folder.value == null) return;
folder.value = null;
hierarchyFolders.value = [];
2023-04-08 02:01:42 +02:00
emit("move-root");
fetch();
}
async function fetch() {
folders.value = [];
files.value = [];
moreFolders.value = false;
moreFiles.value = false;
fetching.value = true;
const foldersMax = 30;
const filesMax = 30;
2023-04-08 02:01:42 +02:00
const foldersPromise = os
.api("drive/folders", {
folderId: folder.value ? folder.value.id : null,
limit: foldersMax + 1,
})
.then((fetchedFolders) => {
if (fetchedFolders.length === foldersMax + 1) {
moreFolders.value = true;
fetchedFolders.pop();
}
return fetchedFolders;
});
2023-04-08 02:01:42 +02:00
const filesPromise = os
.api("drive/files", {
folderId: folder.value ? folder.value.id : null,
type: props.type,
limit: filesMax + 1,
})
.then((fetchedFiles) => {
if (fetchedFiles.length === filesMax + 1) {
moreFiles.value = true;
fetchedFiles.pop();
}
return fetchedFiles;
});
2016-12-28 23:49:51 +01:00
2023-04-08 02:01:42 +02:00
const [fetchedFolders, fetchedFiles] = await Promise.all([
foldersPromise,
filesPromise,
]);
2017-01-11 21:55:38 +01:00
for (const x of fetchedFolders) appendFolder(x);
for (const x of fetchedFiles) appendFile(x);
2018-02-18 04:35:18 +01:00
fetching.value = false;
}
2017-01-11 21:55:38 +01:00
function fetchMoreFiles() {
fetching.value = true;
2017-01-11 21:55:38 +01:00
const max = 30;
2018-02-18 04:35:18 +01:00
// ファイル一覧取得
2023-04-08 02:01:42 +02:00
os.api("drive/files", {
folderId: folder.value ? folder.value.id : null,
type: props.type,
untilId: files.value[files.value.length - 1].id,
2022-07-15 12:15:23 +02:00
limit: max + 1,
2023-04-08 02:01:42 +02:00
}).then((files) => {
if (files.length === max + 1) {
moreFiles.value = true;
files.pop();
} else {
moreFiles.value = false;
}
for (const x of files) appendFile(x);
fetching.value = false;
});
}
function getMenu() {
2023-04-08 02:01:42 +02:00
return [
{
type: "switch",
text: i18n.ts.keepOriginalUploading,
ref: keepOriginal,
},
null,
{
text: i18n.ts.addFile,
type: "label",
},
{
text: i18n.ts.upload,
icon: `${icon("ph-upload-simple")}`,
2023-04-08 02:01:42 +02:00
action: () => {
selectLocalFile();
},
},
{
text: i18n.ts.fromUrl,
icon: `${icon("ph-link-simple")}`,
2023-04-08 02:01:42 +02:00
action: () => {
urlUpload();
},
},
null,
{
text: folder.value ? folder.value.name : i18n.ts.drive,
type: "label",
},
folder.value
? {
text: i18n.ts.renameFolder,
icon: `${icon("ph-cursor-text")}`,
2023-04-08 02:01:42 +02:00
action: () => {
renameFolder(folder.value);
},
}
: undefined,
folder.value
? {
text: i18n.ts.deleteFolder,
icon: `${icon("ph-trash")}`,
2023-04-08 02:01:42 +02:00
action: () => {
deleteFolder(
2023-09-24 06:27:16 +02:00
folder.value as firefish.entities.DriveFolder,
2023-04-08 02:01:42 +02:00
);
},
}
: undefined,
{
text: i18n.ts.createFolder,
icon: `${icon("ph-folder-notch-plus")}`,
2023-04-08 02:01:42 +02:00
action: () => {
createFolder();
},
},
];
}
function showMenu(ev: MouseEvent) {
2023-04-08 02:01:42 +02:00
os.popupMenu(
getMenu(),
2023-07-06 03:28:27 +02:00
(ev.currentTarget ?? ev.target ?? undefined) as HTMLElement | undefined,
2023-04-08 02:01:42 +02:00
);
}
function onContextmenu(ev: MouseEvent) {
os.contextMenu(getMenu(), ev);
}
onMounted(() => {
if (defaultStore.state.enableInfiniteScroll && loadMoreFiles.value) {
nextTick(() => {
ilFilesObserver.observe(loadMoreFiles.value?.$el);
});
}
2017-01-11 21:55:38 +01:00
2023-04-08 02:01:42 +02:00
connection.on("fileCreated", onStreamDriveFileCreated);
connection.on("fileUpdated", onStreamDriveFileUpdated);
connection.on("fileDeleted", onStreamDriveFileDeleted);
connection.on("folderCreated", onStreamDriveFolderCreated);
connection.on("folderUpdated", onStreamDriveFolderUpdated);
connection.on("folderDeleted", onStreamDriveFolderDeleted);
if (props.initialFolder) {
move(props.initialFolder);
} else {
fetch();
}
});
2016-12-28 23:49:51 +01:00
onActivated(() => {
if (defaultStore.state.enableInfiniteScroll) {
nextTick(() => {
ilFilesObserver.observe(loadMoreFiles.value?.$el);
});
2018-02-14 07:54:18 +01:00
}
});
onBeforeUnmount(() => {
connection.dispose();
ilFilesObserver.disconnect();
});
2018-02-14 07:54:18 +01:00
</script>
<style lang="scss" scoped>
.yfudmmck {
2020-11-03 02:27:00 +01:00
display: flex;
flex-direction: column;
height: 100%;
> nav {
2021-07-29 10:10:16 +02:00
display: flex;
z-index: 2;
width: 100%;
Migrate to Vue3 (#6587) * Update reaction.vue * fix bug * wip * wip * wjio * wip * Revert "wip" This reverts commit e427f2160adf4e8a4147006e25a89854edab0033. * wip * wip * wip * Update init.ts * Update drive-window.vue * wip * wip * Use PascalCase for components * Use PascalCase for components * update dep * wip * wip * wip * Update init.ts * wip * Update paging.ts * Update test.vue * watch deep * wip * lint * wip * wip * wip * wip * wiop * wip * Update webpack.config.ts * alllow null poll * wip * wip * wip * wiop * UI redesign & refactor (#6714) * wip * wip * wip * wip * wip * Update drive.vue * Update word-mute.vue * wip * wip * wip * clean up * wip * Update default.vue * wip * Update notes.vue * Update mfm.ts * Update index.home.vue * Update post-form.vue * Update post-form-attaches.vue * wip * Update post-form.vue * Update sidebar.vue * wip * wip * Update index.vue * wip * Update default.vue * Update index.vue * Update index.vue * wip * Update post-form-attaches.vue * Update note.vue * wip * clean up * Update notes.vue * wip * wip * Update ja-JP.yml * wip * wip * Update index.vue * wip * wip * wip * wip * wip * wip * wip * wip * Update default.vue * wip * Update _dark.json5 * wip * wip * wip * clean up * wip * wip * Update index.vue * Update test.vue * wip * wip * fix * wip * wip * wip * wip * clena yop * wip * wip * Update store.ts * Update messaging-room.vue * Update default.widgets.vue * fix * wip * wip * Update modal.vue * wip * Update os.ts * Update os.ts * Update deck.vue * Update init.ts * wip * Update ja-JP.yml * v-sizeは単にwindowのresizeを監視するだけで良いかもしれない * Update modal.vue * wip * Update tooltip.ts * wip * wip * wip * wip * wip * Update image-viewer.vue * wip * wip * Update style.scss * Update style.scss * Update visitor.vue * wip * Update init.ts * Update init.ts * wip * wip * Update visitor.vue * Update visitor.vue * Update visitor.vue * Update visitor.vue * wip * wip * Update modal.vue * Update header.vue * Update menu.vue * Update about.vue * Update about-misskey.vue * wip * wip * Update visitor.vue * Update tooltip.ts * wip * Update drive.vue * wip * Update style.scss * Update header.vue * wip * wip * Update users.user.vue * Update announcements.vue * wip * wip * wip * Update emojis.vue * wip * Update emojis.vue * Update style.scss * Update users.vue * wip * Update style.scss * wip * Update welcome.entrance.vue * Update radio.vue * Update size.ts * Update emoji-edit-dialog.vue * wip * Update emojis.vue * wip * Update emojis.vue * Update emojis.vue * Update emojis.vue * wip * wip * wip * wip * Update file-dialog.vue * wip * wip * Update token-generate-window.vue * Update notification-setting-window.vue * wip * wip * Update _error_.vue * Update ja-JP.yml * wip * wip * Update store.ts * Update emojis.vue * Update emojis.vue * Update emojis.vue * Update announcements.vue * Update store.ts * wip * Update page-editor.vue * wip * wip * Update modal.vue * wip * Update select-file.ts * Update timeline.vue * Update emojis.vue * Update os.ts * wip * Update user-select.vue * Update mfm.ts * Update get-file-info.ts * Update drive.vue * Update init.ts * Update mfm.ts * wip * wip * Update window.vue * Update note.vue * wip * wip * Update user-info.vue * wip * wip * wip * wip * wip * Update header.vue * Update header.vue * wip * Update explore.vue * wip * wip * wip * Update webpack.config.ts * wip * wip * wip * wip * wip * wip * Update autocomplete.ts * wip * wip * wip * Update toast.vue * wip * Update post-form-dialog.vue * wip * wip * wip * wip * wip * Update users.vue * wip * Update explore.vue * wip * wip * wip * Update package.json * wip * Update icon-dialog.vue * wip * wip * Update user-preview.ts * wip * wip * wip * wip * wip * Update instance.vue * Update user-name.vue * Update federation.vue * Update instance.vue * wip * wip * Update tag.vue * wip * wip * wip * wip * wip * Update instance.vue * wip * Update os.ts * Update os.ts * wip * wip * wip * Update router.ts * wip * Update init.ts * Update note.vue * Update messages.vue * wip * wip * wip * wip * wip * google * wip * wip * wip * wip * Update theme-editor.vue * wip * wip * Update room.vue * Update channel-editor.vue * wip * Update window.vue * Update window.vue * wip * Update window.vue * Update window.vue * wip * Update menu.vue * wip * wip * wip * wip * Update messaging-room.vue * wip * Update post-form.vue * Update default.widgets.vue * Update window.vue * wip
2020-10-17 13:12:00 +02:00
padding: 0 8px;
box-sizing: border-box;
overflow: auto;
font-size: 0.9em;
box-shadow: 0 1px 0 var(--divider);
position: fixed;
background-color: var(--bg);
2023-04-08 02:01:42 +02:00
&,
* {
user-select: none;
}
> .path {
display: inline-block;
vertical-align: bottom;
2022-07-15 12:15:23 +02:00
line-height: 42px;
white-space: nowrap;
> * {
display: inline-block;
margin: 0;
padding: 0 8px;
2022-07-15 12:15:23 +02:00
line-height: 42px;
cursor: pointer;
* {
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;
> i {
margin: 0;
}
}
}
}
2021-07-29 10:10:16 +02:00
> .menu {
margin-left: auto;
2021-12-10 02:46:29 +01:00
padding: 0 12px;
2021-07-29 10:10:16 +02:00
}
}
> .main {
2020-11-03 02:27:00 +01:00
flex: 1;
overflow: auto;
padding: var(--margin);
margin-top: 40px;
2023-04-08 02:01:42 +02:00
&,
* {
user-select: none;
}
2018-02-14 07:54:18 +01:00
&.fetching {
cursor: wait !important;
* {
pointer-events: none;
}
> .contents {
opacity: 0.5;
}
}
&.uploading {
height: calc(100% - 38px - 100px);
}
> .contents {
> .folders,
> .files {
display: flex;
flex-wrap: wrap;
> .folder,
> .file {
flex-grow: 1;
width: 128px;
margin: 4px;
box-sizing: border-box;
}
> .padding {
flex-grow: 1;
pointer-events: none;
width: 128px + 8px;
2018-02-14 07:54:18 +01:00
}
}
> .empty {
padding: 16px;
text-align: center;
pointer-events: none;
opacity: 0.5;
2018-02-14 07:54:18 +01:00
> p {
margin: 0;
}
}
}
}
2018-02-14 07:54:18 +01:00
> .dropzone {
position: absolute;
left: 0;
top: 38px;
width: 100%;
height: calc(100% - 38px);
border: dashed 2px var(--focus);
pointer-events: none;
}
> input {
display: none;
}
}
2018-02-14 07:54:18 +01:00
</style>