build: switch from yarn to npm to manage js dependencies and move js contents to root
yarn v1 is being deprecated and starts to have some issues Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
167
src/components/Resource/DraggableList.vue
Normal file
167
src/components/Resource/DraggableList.vue
Normal file
@@ -0,0 +1,167 @@
|
||||
<template>
|
||||
<section class="bg-white dark:bg-zinc-700 p-2 pt-0.5">
|
||||
<h1>{{ t("Resources") }}</h1>
|
||||
<p v-if="isRoot">
|
||||
{{ t("A place to store links to documents or resources of any type.") }}
|
||||
</p>
|
||||
<div class="pl-6 mt-2 flex items-center gap-3">
|
||||
<o-checkbox v-model="checkedAll" v-if="resources.length > 0">
|
||||
<span class="sr-only">{{ t("Select all resources") }}</span>
|
||||
</o-checkbox>
|
||||
<div
|
||||
class="flex items-center gap-3"
|
||||
v-if="validCheckedResources.length > 0"
|
||||
>
|
||||
<small>
|
||||
{{
|
||||
t(
|
||||
"No resources selected",
|
||||
{
|
||||
count: validCheckedResources.length,
|
||||
},
|
||||
validCheckedResources.length
|
||||
)
|
||||
}}
|
||||
</small>
|
||||
<o-button
|
||||
variant="danger"
|
||||
icon-right="delete"
|
||||
size="small"
|
||||
@click="deleteMultipleResources"
|
||||
>{{ t("Delete") }}</o-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<draggable
|
||||
:list="resources"
|
||||
:sort="false"
|
||||
:group="groupObject"
|
||||
v-if="resources.length > 0"
|
||||
tag="transition-group"
|
||||
item-key="id"
|
||||
>
|
||||
<template #item="{ element }">
|
||||
<div class="flex border m-2 p-2 items-center">
|
||||
<div
|
||||
class="resource-checkbox px-2"
|
||||
:class="{ checked: checkedResources[element.id as string] }"
|
||||
>
|
||||
<o-checkbox v-model="checkedResources[element.id as string]">
|
||||
<span class="sr-only">{{ t("Select this resource") }}</span>
|
||||
</o-checkbox>
|
||||
</div>
|
||||
<resource-item
|
||||
:resource="element"
|
||||
v-if="element.type !== 'folder'"
|
||||
@delete="emit('delete', $event)"
|
||||
@rename="emit('rename', $event)"
|
||||
@move="emit('move', $event)"
|
||||
/>
|
||||
<folder-item
|
||||
:resource="element"
|
||||
:group="group"
|
||||
@delete="emit('delete', $event)"
|
||||
@rename="emit('rename', $event)"
|
||||
@move="emit('move', $event)"
|
||||
v-else
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</draggable>
|
||||
<EmptyContent icon="link" :inline="true" v-if="resources.length === 0">
|
||||
{{ t("No resources in this folder") }}
|
||||
<template #desc>
|
||||
{{ t("You can add resources by using the button above.") }}
|
||||
</template>
|
||||
</EmptyContent>
|
||||
</section>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import ResourceItem from "@/components/Resource/ResourceItem.vue";
|
||||
import FolderItem from "@/components/Resource/FolderItem.vue";
|
||||
import { reactive, ref, watch } from "vue";
|
||||
import { IResource } from "@/types/resource";
|
||||
import Draggable from "zhyswan-vuedraggable";
|
||||
import { IGroup } from "@/types/actor";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import EmptyContent from "@/components/Utils/EmptyContent.vue";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{ resources: IResource[]; isRoot: boolean; group: IGroup }>(),
|
||||
{ isRoot: false }
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "move", resource: IResource): void;
|
||||
(e: "rename", resource: IResource): void;
|
||||
(e: "delete", resourceID: string): void;
|
||||
}>();
|
||||
|
||||
const { t } = useI18n({ useScope: "global" });
|
||||
|
||||
const groupObject: Record<string, unknown> = {
|
||||
name: "resources",
|
||||
pull: "clone",
|
||||
put: true,
|
||||
};
|
||||
|
||||
const checkedResources = reactive<{ [key: string]: boolean }>({});
|
||||
|
||||
const validCheckedResources = ref<string[]>([]);
|
||||
|
||||
const checkedAll = ref(false);
|
||||
|
||||
const deleteMultipleResources = async (): Promise<void> => {
|
||||
validCheckedResources.value.forEach((resourceID) => {
|
||||
emit("delete", resourceID);
|
||||
});
|
||||
};
|
||||
|
||||
watch(checkedAll, () => {
|
||||
props.resources.forEach(({ id }) => {
|
||||
if (!id) return;
|
||||
checkedResources[id] = checkedAll.value;
|
||||
});
|
||||
});
|
||||
|
||||
watch(checkedResources, (newCheckedResources) => {
|
||||
const newValidCheckedResources: string[] = [];
|
||||
Object.entries(newCheckedResources).forEach(([key, value]) => {
|
||||
if (value) {
|
||||
newValidCheckedResources.push(key);
|
||||
}
|
||||
});
|
||||
validCheckedResources.value = newValidCheckedResources;
|
||||
});
|
||||
|
||||
// const deleteResource = (resourceID: string) => {
|
||||
// validCheckedResources.value = validCheckedResources.value.filter(
|
||||
// (id) => id !== resourceID
|
||||
// );
|
||||
// delete checkedResources.value[resourceID];
|
||||
// emit("delete", resourceID);
|
||||
// };
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@use "@/styles/_mixins" as *;
|
||||
.resource-item,
|
||||
.new-resource-preview {
|
||||
display: flex;
|
||||
font-size: 14px;
|
||||
border: 1px solid #c0cdd9;
|
||||
border-radius: 4px;
|
||||
// color: #444b5d;
|
||||
margin-top: 14px;
|
||||
margin-bottom: 14px;
|
||||
|
||||
.resource-checkbox {
|
||||
align-self: center;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
&:hover .resource-checkbox,
|
||||
.resource-checkbox.checked {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
183
src/components/Resource/FolderItem.vue
Normal file
183
src/components/Resource/FolderItem.vue
Normal file
@@ -0,0 +1,183 @@
|
||||
<template>
|
||||
<div class="resource-wrapper bg-white dark:bg-transparent" dir="auto">
|
||||
<router-link
|
||||
:to="{
|
||||
name: RouteName.RESOURCE_FOLDER,
|
||||
params: {
|
||||
path: resourcePathArray(resource),
|
||||
preferredUsername: usernameWithDomain(group),
|
||||
},
|
||||
}"
|
||||
>
|
||||
<div class="preview text-mbz-purple dark:text-mbz-purple-300">
|
||||
<Folder :size="48" />
|
||||
</div>
|
||||
<div class="body">
|
||||
<h3>{{ resource.title }}</h3>
|
||||
<span class="host" v-if="inline && resource.updatedAt">{{
|
||||
formatDateTimeString(resource.updatedAt?.toString())
|
||||
}}</span>
|
||||
</div>
|
||||
<draggable
|
||||
v-if="!inline"
|
||||
class="dropzone"
|
||||
v-model="list"
|
||||
itemKey="id"
|
||||
:sort="false"
|
||||
:group="groupObject"
|
||||
@change="onChange"
|
||||
>
|
||||
<template #item="{ element }">
|
||||
<div>{{ element.name }}</div>
|
||||
</template>
|
||||
</draggable>
|
||||
</router-link>
|
||||
<resource-dropdown
|
||||
class="actions"
|
||||
v-if="!inline"
|
||||
@delete="emit('delete', resource.id as string)"
|
||||
@move="emit('move', resource)"
|
||||
@rename="emit('rename', resource)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { useRouter } from "vue-router";
|
||||
import Draggable from "zhyswan-vuedraggable";
|
||||
import { IResource } from "@/types/resource";
|
||||
import RouteName from "@/router/name";
|
||||
import { IMinimalActor, usernameWithDomain } from "@/types/actor";
|
||||
import ResourceDropdown from "./ResourceDropdown.vue";
|
||||
import { UPDATE_RESOURCE } from "@/graphql/resources";
|
||||
import { ComputedRef, computed, inject, ref } from "vue";
|
||||
import { formatDateTimeString } from "@/filters/datetime";
|
||||
import { useMutation } from "@vue/apollo-composable";
|
||||
import { resourcePathArray } from "@/components/Resource/utils";
|
||||
import Folder from "vue-material-design-icons/Folder.vue";
|
||||
import { Snackbar } from "@/plugins/snackbar";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
resource: IResource;
|
||||
group: IMinimalActor;
|
||||
inline?: boolean;
|
||||
}>(),
|
||||
{ inline: false }
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "move", resource: IResource): void;
|
||||
(e: "rename", resource: IResource): void;
|
||||
(e: "delete", resourceID: string): void;
|
||||
}>();
|
||||
|
||||
const list = ref([]);
|
||||
|
||||
const groupObject: ComputedRef<Record<string, unknown>> = computed(() => ({
|
||||
name: `folder-${props.resource?.title}`,
|
||||
pull: false,
|
||||
put: ["resources"],
|
||||
}));
|
||||
|
||||
const onChange = async (evt: any) => {
|
||||
if (evt.added && evt.added.element) {
|
||||
const movedResource = evt.added.element as IResource;
|
||||
console.debug(
|
||||
`Moving resource « ${movedResource.title} » to path « ${props.resource.path} » (new parent ${props.resource.id})`
|
||||
);
|
||||
moveResource({
|
||||
id: movedResource.id,
|
||||
path: props.resource.path,
|
||||
parentId: props.resource.id,
|
||||
});
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const {
|
||||
mutate: moveResource,
|
||||
onDone: onMovedResource,
|
||||
onError: onMovedResourceError,
|
||||
} = useMutation<{ updateResource: IResource }>(UPDATE_RESOURCE);
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
onMovedResource(({ data }) => {
|
||||
if (data?.updateResource && props.resource.path) {
|
||||
return router.push({
|
||||
name: RouteName.RESOURCE_FOLDER,
|
||||
params: {
|
||||
path: resourcePathArray(props.resource),
|
||||
preferredUsername: props.group.preferredUsername,
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
const snackbar = inject<Snackbar>("snackbar");
|
||||
|
||||
onMovedResourceError((e) => {
|
||||
snackbar?.open({
|
||||
message: e.message,
|
||||
variant: "danger",
|
||||
position: "bottom",
|
||||
});
|
||||
return undefined;
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.resource-wrapper {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
|
||||
.actions {
|
||||
flex: 0;
|
||||
display: block;
|
||||
margin: auto 1rem auto 2rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.dropzone {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
a {
|
||||
display: flex;
|
||||
font-size: 14px;
|
||||
// color: #444b5d;
|
||||
text-decoration: none;
|
||||
overflow: hidden;
|
||||
flex: 1;
|
||||
position: relative;
|
||||
|
||||
.preview {
|
||||
flex: 0 0 50px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.body {
|
||||
padding: 8px;
|
||||
flex: 1 1 auto;
|
||||
overflow: hidden;
|
||||
|
||||
h3 {
|
||||
white-space: nowrap;
|
||||
display: block;
|
||||
font-weight: 500;
|
||||
margin-bottom: 5px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
30
src/components/Resource/ResourceDropdown.vue
Normal file
30
src/components/Resource/ResourceDropdown.vue
Normal file
@@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<o-dropdown aria-role="list" position="bottom-left">
|
||||
<template #trigger>
|
||||
<DotsHorizontal />
|
||||
</template>
|
||||
|
||||
<o-dropdown-item
|
||||
aria-role="listitem"
|
||||
@click="$emit('rename')"
|
||||
class="inline-flex"
|
||||
>
|
||||
<Pencil />
|
||||
{{ $t("Rename") }}
|
||||
</o-dropdown-item>
|
||||
<o-dropdown-item aria-role="listitem" @click="$emit('move')">
|
||||
<FolderMove />
|
||||
{{ $t("Move") }}
|
||||
</o-dropdown-item>
|
||||
<o-dropdown-item aria-role="listitem" @click="$emit('delete')">
|
||||
<Delete />
|
||||
{{ $t("Delete") }}
|
||||
</o-dropdown-item>
|
||||
</o-dropdown>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import Pencil from "vue-material-design-icons/Pencil.vue";
|
||||
import FolderMove from "vue-material-design-icons/FolderMove.vue";
|
||||
import Delete from "vue-material-design-icons/Delete.vue";
|
||||
import DotsHorizontal from "vue-material-design-icons/DotsHorizontal.vue";
|
||||
</script>
|
||||
154
src/components/Resource/ResourceItem.vue
Normal file
154
src/components/Resource/ResourceItem.vue
Normal file
@@ -0,0 +1,154 @@
|
||||
<template>
|
||||
<div
|
||||
class="flex flex-1 items-center w-full bg-white dark:bg-transparent"
|
||||
dir="auto"
|
||||
>
|
||||
<a :href="resource.resourceUrl" target="_blank">
|
||||
<div
|
||||
class="min-w-fit relative flex items-center justify-center text-mbz-purple dark:text-mbz-purple-300"
|
||||
>
|
||||
<div
|
||||
v-if="
|
||||
resource.type &&
|
||||
Object.keys(mapServiceTypeToIcon).includes(resource.type)
|
||||
"
|
||||
>
|
||||
<o-icon
|
||||
:icon="mapServiceTypeToIcon[resource.type]"
|
||||
size="large"
|
||||
customSize="48"
|
||||
/>
|
||||
</div>
|
||||
<img
|
||||
v-else-if="resource.metadata && resource.metadata.imageRemoteUrl"
|
||||
:src="resource.metadata.imageRemoteUrl"
|
||||
alt=""
|
||||
height="48"
|
||||
width="48"
|
||||
/>
|
||||
<div class="preview-type" v-else>
|
||||
<Link :size="48" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="body flex-1 px-1 pb-1">
|
||||
<div class="flex items-center gap-1 max-w-[65vw]">
|
||||
<img
|
||||
class="favicon"
|
||||
alt=""
|
||||
v-if="resource.metadata && resource.metadata.faviconUrl"
|
||||
:src="resource.metadata.faviconUrl"
|
||||
/>
|
||||
<h3 class="dark:text-white">{{ resource.title }}</h3>
|
||||
</div>
|
||||
<div class="metadata-wrapper">
|
||||
<span class="host" v-if="!inline || preview">{{ urlHostname }}</span>
|
||||
<span
|
||||
class="hidden md:inline"
|
||||
:class="{ inline }"
|
||||
v-if="resource.updatedAt || resource.publishedAt"
|
||||
>{{
|
||||
formatDateTimeString(
|
||||
resource.updatedAt ?? resource.publishedAt ?? ""
|
||||
)
|
||||
}}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<resource-dropdown
|
||||
class="flex-0 block mx-auto my-2 cursor-pointer mr-2"
|
||||
v-if="!inline && !preview"
|
||||
@delete="emit('delete', resource.id as string)"
|
||||
@move="emit('move', resource)"
|
||||
@rename="emit('rename', resource)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { IResource, mapServiceTypeToIcon } from "@/types/resource";
|
||||
import ResourceDropdown from "@/components/Resource/ResourceDropdown.vue";
|
||||
import { computed } from "vue";
|
||||
import { formatDateTimeString } from "@/filters/datetime";
|
||||
import Link from "vue-material-design-icons/Link.vue";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
resource: IResource;
|
||||
inline?: boolean;
|
||||
preview?: boolean;
|
||||
}>(),
|
||||
{ inline: false, preview: false }
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "move", resource: IResource): void;
|
||||
(e: "rename", resource: IResource): void;
|
||||
(e: "delete", resourceID: string): void;
|
||||
}>();
|
||||
|
||||
// const list = ref([]);
|
||||
|
||||
const urlHostname = computed((): string | undefined => {
|
||||
if (props.resource?.resourceUrl) {
|
||||
return new URL(props.resource.resourceUrl).hostname.replace(/^(www\.)/, "");
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@use "@/styles/_mixins" as *;
|
||||
|
||||
a {
|
||||
display: flex;
|
||||
font-size: 14px;
|
||||
// color: #444b5d;
|
||||
text-decoration: none;
|
||||
overflow: hidden;
|
||||
flex: 1;
|
||||
|
||||
.body {
|
||||
img.favicon {
|
||||
display: inline-block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
// @include margin-right(6px);
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
h3 {
|
||||
white-space: nowrap;
|
||||
display: inline-block;
|
||||
font-weight: 500;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
text-decoration: none;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.metadata-wrapper {
|
||||
max-width: calc(100vw - 122px);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
span {
|
||||
&:last-child::before {
|
||||
content: "⋅";
|
||||
padding: 0 5px;
|
||||
}
|
||||
&:first-child::before {
|
||||
content: "";
|
||||
padding: initial;
|
||||
}
|
||||
|
||||
&.host,
|
||||
&.published-at {
|
||||
font-size: 13px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
199
src/components/Resource/ResourceSelector.vue
Normal file
199
src/components/Resource/ResourceSelector.vue
Normal file
@@ -0,0 +1,199 @@
|
||||
<template>
|
||||
<div v-if="resource">
|
||||
<article class="">
|
||||
<h2 class="mb-2">
|
||||
{{
|
||||
t('Move "{resourceName}"', { resourceName: initialResource.title })
|
||||
}}
|
||||
</h2>
|
||||
<a
|
||||
class="cursor-pointer flex gap-1 items-center border p-2"
|
||||
@click="goUp()"
|
||||
v-if="resource.parent"
|
||||
>
|
||||
<ChevronUp :size="16" />
|
||||
{{ t("Parent folder") }}
|
||||
</a>
|
||||
<a
|
||||
class="cursor-pointer flex gap-1 items-center border p-2"
|
||||
@click="resourcePath.path = '/'"
|
||||
v-else-if="resourcePath?.path && resourcePath?.path.length > 1"
|
||||
>
|
||||
<ChevronUp :size="16" />
|
||||
{{ t("Parent folder") }}
|
||||
</a>
|
||||
<template v-if="resource.children">
|
||||
<a
|
||||
class="cursor-pointer flex flex-wrap gap-1 p-2 border"
|
||||
v-for="element in resource.children.elements"
|
||||
:class="{
|
||||
clickable:
|
||||
element.type === 'folder' && element.id !== initialResource.id,
|
||||
}"
|
||||
:key="element.id"
|
||||
@click="goDown(element)"
|
||||
>
|
||||
<p class="truncate flex gap-1 items-center">
|
||||
<Folder :size="16" v-if="element.type === 'folder'" />
|
||||
<Link :size="16" v-else />
|
||||
<span>{{ element.title }}</span>
|
||||
</p>
|
||||
<span v-if="element.id === initialResource.id">
|
||||
<em v-if="element.type === 'folder'"> {{ t("(this folder)") }}</em>
|
||||
<em v-else> {{ t("(this link)") }}</em>
|
||||
</span>
|
||||
</a>
|
||||
</template>
|
||||
<p class="" v-if="resource.children && resource.children.total === 0">
|
||||
{{ t("No resources in this folder") }}
|
||||
</p>
|
||||
<o-pagination
|
||||
v-if="resource.children && resource.children.total > RESOURCES_PER_PAGE"
|
||||
:total="resource.children.total"
|
||||
v-model:current="page"
|
||||
size="small"
|
||||
:per-page="RESOURCES_PER_PAGE"
|
||||
:aria-next-label="t('Next page')"
|
||||
:aria-previous-label="t('Previous page')"
|
||||
:aria-page-label="t('Page')"
|
||||
:aria-current-label="t('Current page')"
|
||||
/>
|
||||
</article>
|
||||
<div class="flex gap-2 mt-2">
|
||||
<o-button variant="text" @click="emit('close-move-modal')">{{
|
||||
t("Cancel")
|
||||
}}</o-button>
|
||||
<o-button
|
||||
variant="primary"
|
||||
@click="updateResource"
|
||||
:disabled="moveDisabled"
|
||||
><template v-if="resource.path === '/'">
|
||||
{{ t("Move resource to the root folder") }}
|
||||
</template>
|
||||
<template v-else
|
||||
>{{ t("Move resource to {folder}", { folder: resource.title }) }}
|
||||
</template></o-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { useQuery } from "@vue/apollo-composable";
|
||||
import { computed, reactive, ref, watch } from "vue";
|
||||
import { GET_RESOURCE } from "../../graphql/resources";
|
||||
import { IResource } from "../../types/resource";
|
||||
import Folder from "vue-material-design-icons/Folder.vue";
|
||||
import Link from "vue-material-design-icons/Link.vue";
|
||||
import ChevronUp from "vue-material-design-icons/ChevronUp.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const props = defineProps<{ initialResource: IResource; username: string }>();
|
||||
const emit = defineEmits(["update-resource", "close-move-modal"]);
|
||||
|
||||
const { t } = useI18n({ useScope: "global" });
|
||||
|
||||
const initialResourceProp = computed(() => props.initialResource);
|
||||
const usernameProp = computed(() => props.username);
|
||||
|
||||
const resourcePath = reactive<{
|
||||
path: string | undefined;
|
||||
username: string;
|
||||
id: string | undefined;
|
||||
}>({
|
||||
id: initialResourceProp.value.parent?.id,
|
||||
path: initialResourceProp.value.parent?.path,
|
||||
username: usernameProp.value,
|
||||
});
|
||||
|
||||
const RESOURCES_PER_PAGE = 10;
|
||||
const page = ref(1);
|
||||
|
||||
const { result: resourceResult, refetch } = useQuery<{ resource: IResource }>(
|
||||
GET_RESOURCE,
|
||||
() => {
|
||||
if (resourcePath?.path) {
|
||||
return {
|
||||
path: resourcePath?.path,
|
||||
username: usernameProp.value,
|
||||
page: page.value,
|
||||
limit: RESOURCES_PER_PAGE,
|
||||
};
|
||||
}
|
||||
return { path: "/", username: usernameProp.value };
|
||||
}
|
||||
);
|
||||
|
||||
const resource = computed(() => resourceResult.value?.resource);
|
||||
|
||||
const goDown = (element: IResource): void => {
|
||||
if (
|
||||
element.type === "folder" &&
|
||||
element.id !== initialResourceProp.value.id
|
||||
) {
|
||||
resourcePath.id = element.id;
|
||||
resourcePath.path = element.path;
|
||||
console.debug("Gone into folder", resourcePath);
|
||||
}
|
||||
};
|
||||
|
||||
watch(initialResourceProp, () => {
|
||||
if (initialResourceProp.value) {
|
||||
resourcePath.id = props.initialResource?.parent?.id;
|
||||
resourcePath.path = props.initialResource?.parent?.path;
|
||||
refetch();
|
||||
}
|
||||
});
|
||||
|
||||
const updateResource = (): void => {
|
||||
console.debug("Emitting updateResource from folder", resourcePath);
|
||||
const parent = resourcePath?.path === "/" ? null : resourcePath;
|
||||
emit(
|
||||
"update-resource",
|
||||
{
|
||||
id: initialResourceProp.value.id,
|
||||
title: initialResourceProp.value.title,
|
||||
parent: parent,
|
||||
path: parent?.path ?? "/",
|
||||
},
|
||||
initialResourceProp.value.parent
|
||||
);
|
||||
};
|
||||
|
||||
const moveDisabled = computed((): boolean | undefined => {
|
||||
return (
|
||||
(initialResourceProp.value.parent &&
|
||||
resourcePath &&
|
||||
initialResourceProp.value.parent.path === resourcePath.path) ||
|
||||
(initialResourceProp.value.parent === undefined &&
|
||||
resourcePath &&
|
||||
resourcePath.path === "/")
|
||||
);
|
||||
});
|
||||
|
||||
const goUp = () => {
|
||||
resourcePath.id = resource.value?.parent?.id;
|
||||
resourcePath.path = resource.value?.parent?.path;
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.panel {
|
||||
a.panel-block {
|
||||
cursor: default;
|
||||
|
||||
&.clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-primary .panel-heading {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
.buttons {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
nav.pagination {
|
||||
margin: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
15
src/components/Resource/utils.ts
Normal file
15
src/components/Resource/utils.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { IResource } from "@/types/resource";
|
||||
|
||||
export const resourcePath = (resource: IResource | undefined): string => {
|
||||
const path = resource?.path ?? undefined;
|
||||
if (path && path[0] === "/") {
|
||||
return path.slice(1);
|
||||
}
|
||||
return path ?? "";
|
||||
};
|
||||
|
||||
export const resourcePathArray = (
|
||||
resource: IResource | undefined
|
||||
): string[] => {
|
||||
return resourcePath(resource).split("/");
|
||||
};
|
||||
Reference in New Issue
Block a user