Browse Source

feat(gui-v2,deps): add attachment views carousel

pull/2972/head
braks 2 years ago
parent
commit
21e31f9c00
  1. 1
      packages/nc-gui-v2/components.d.ts
  2. 128
      packages/nc-gui-v2/components/cell/attachment/Carousel.vue
  3. 2
      packages/nc-gui-v2/components/cell/attachment/Modal.vue
  4. 13
      packages/nc-gui-v2/components/cell/attachment/index.vue
  5. 7
      packages/nc-gui-v2/components/cell/attachment/sort.ts
  6. 3
      packages/nc-gui-v2/components/cell/attachment/utils.ts

1
packages/nc-gui-v2/components.d.ts vendored

@ -11,6 +11,7 @@ declare module '@vue/runtime-core' {
AAutoComplete: typeof import('ant-design-vue/es')['AutoComplete']
AButton: typeof import('ant-design-vue/es')['Button']
ACard: typeof import('ant-design-vue/es')['Card']
ACarousel: typeof import('ant-design-vue/es')['Carousel']
ACheckbox: typeof import('ant-design-vue/es')['Checkbox']
ACol: typeof import('ant-design-vue/es')['Col']
ACollapse: typeof import('ant-design-vue/es')['Collapse']

128
packages/nc-gui-v2/components/cell/attachment/Carousel.vue

@ -0,0 +1,128 @@
<script lang="ts" setup>
import type { Carousel as AntCarousel } from 'ant-design-vue'
import { onKeyDown } from '@vueuse/core'
import { useAttachmentCell } from './utils'
import { isImage } from '~/utils'
import { computed, ref } from '#imports'
import MaterialSymbolsArrowCircleRightRounded from '~icons/material-symbols/arrow-circle-right-rounded'
import MaterialSymbolsArrowCircleLeftRounded from '~icons/material-symbols/arrow-circle-left-rounded'
import MdiCloseCircle from '~icons/mdi/close-circle'
const { selectedImage, visibleItems, downloadFile } = useAttachmentCell()!
const carouselRef = ref<typeof AntCarousel>()
const imageItems = computed(() => visibleItems.value.filter((item) => isImage(item.title, item.mimetype)))
onKeyDown(
(e) => ['Left', 'ArrowLeft', 'A'].includes(e.key),
() => {
if (carouselRef.value) carouselRef.value.prev()
},
)
onKeyDown(
(e) => ['Right', 'ArrowRight', 'D'].includes(e.key),
() => {
if (carouselRef.value) carouselRef.value.next()
},
)
function onSlideChange(index: number) {
selectedImage.value = imageItems.value[index]
}
</script>
<template>
<general-overlay v-model="selectedImage">
<template v-if="selectedImage">
<div class="p-12 text-center relative">
<div class="text-white group absolute top-5 right-5">
<MdiCloseCircle class="group-hover:text-red-500 cursor-pointer text-4xl" @click.stop="selectedImage = false" />
</div>
<div
class="select-none group hover:ring active:ring-pink-500 cursor-pointer leading-8 inline-block px-3 py-1 bg-gray-300 text-white mb-4 text-center rounded shadow"
@click.stop="downloadFile(selectedImage)"
>
<h3 class="group-hover:text-primary">{{ selectedImage && selectedImage.title }}</h3>
</div>
<a-carousel
v-if="!!selectedImage"
ref="carouselRef"
dots-class="slick-dots slick-thumb"
:after-change="onSlideChange"
arrows
>
<template #prevArrow>
<div class="custom-slick-arrow left-2 z-1">
<MaterialSymbolsArrowCircleLeftRounded />
</div>
</template>
<template #nextArrow>
<div class="custom-slick-arrow !right-2 z-1">
<MaterialSymbolsArrowCircleRightRounded />
</div>
</template>
<template #customPaging="props">
<a>
<img
class="!block w-full"
:alt="imageItems[props.i].title || `#${props.i}`"
:src="imageItems[props.i].url || imageItems[props.i].data"
/>
</a>
</template>
<div v-for="(item, i) of imageItems" :key="item.url">
<img class="!block w-full max-w-75vw max-h-75vh" :alt="item.title || `#${i}`" :src="item.url || item.data" />
</div>
</a-carousel>
</div>
</template>
</general-overlay>
</template>
<style scoped>
.ant-carousel :deep(.slick-dots) {
@apply relative mt-4;
}
.ant-carousel :deep(.slick-slide) {
@apply w-full;
}
.ant-carousel :deep(.slick-slide img) {
@apply border-1 m-auto;
}
.ant-carousel :deep(.slick-thumb) {
@apply bottom-2;
}
.ant-carousel :deep(.slick-thumb li) {
@apply w-[60px] h-[45px];
}
.ant-carousel :deep(.slick-thumb li img) {
@apply w-full h-full block;
filter: grayscale(100%);
}
.ant-carousel :deep .slick-thumb li.slick-active img {
filter: grayscale(0%);
}
.ant-carousel :deep(.slick-arrow.custom-slick-arrow) {
@apply text-4xl text-white hover:text-primary active:text-pink-500 opacity-100 cursor-pointer z-1;
}
.ant-carousel :deep(.custom-slick-arrow:before) {
display: none;
}
.ant-carousel :deep(.custom-slick-arrow:hover) {
opacity: 0.5;
}
</style>

2
packages/nc-gui-v2/components/cell/attachment/Modal.vue

@ -25,7 +25,7 @@ const {
onDrop,
downloadFile,
updateModelValue,
} = useAttachmentCell()
} = useAttachmentCell()!
// todo: replace placeholder var
const isLocked = ref(false)

13
packages/nc-gui-v2/components/cell/attachment/index.vue

@ -2,6 +2,7 @@
import { useProvideAttachmentCell } from './utils'
import Modal from './Modal.vue'
import { useSortable } from './sort'
import Carousel from './Carousel.vue'
import { ref, useDropZone, watch } from '#imports'
import { isImage, openLink } from '~/utils'
import MaterialSymbolsAttachFile from '~icons/material-symbols/attach-file'
@ -26,7 +27,8 @@ const dropZoneRef = ref<HTMLDivElement>()
const sortableRef = ref<HTMLDivElement>()
const { modalVisible, attachments, visibleItems, onDrop, isLoading, open, FileIcon } = useProvideAttachmentCell(updateModelValue)
const { modalVisible, attachments, visibleItems, onDrop, isLoading, open, FileIcon, selectedImage } =
useProvideAttachmentCell(updateModelValue)
const { dragging } = useSortable(sortableRef, visibleItems, updateModelValue)
@ -43,17 +45,18 @@ watch(
)
function updateModelValue(data: string | Record<string, any>) {
console.log(data)
emits('update:modelValue', typeof data !== 'string' ? JSON.stringify(data) : data)
}
const selectImage = (file: any, i: unknown) => {
// todo: implement
const selectImage = (file: any) => {
selectedImage.value = file
}
</script>
<template>
<div ref="dropZoneRef" class="nc-attachment-cell flex-1 color-transition flex items-center justify-between gap-1">
<Carousel />
<template v-if="!dragging && isOverDropZone">
<div
class="w-full h-full flex items-center justify-center p-1 rounded gap-1 bg-gradient-to-t from-primary/10 via-primary/25 to-primary/10 !text-primary"
@ -102,7 +105,7 @@ const selectImage = (file: any, i: unknown) => {
v-if="isImage(item.title, item.mimetype)"
:alt="item.title || `#${i}`"
:src="item.url || item.data"
@click="selectImage(item.url || item.data, i)"
@click="selectImage(item)"
/>
<component :is="FileIcon(item.icon)" v-else-if="item.icon" @click="openLink(item.url || item.data)" />

7
packages/nc-gui-v2/components/cell/attachment/sort.ts

@ -39,8 +39,6 @@ export function useSortable(
// todo: replace with vuedraggable
const initSortable = (el: HTMLElement) => {
if (sortable) sortable.destroy()
sortable = new Sortable(el, {
handle: '.nc-attachment',
ghostClass: 'ghost',
@ -50,11 +48,12 @@ export function useSortable(
}
watchPostEffect((onCleanup) => {
const _element = unref(element)
onCleanup(() => {
if (sortable) sortable.destroy()
if (_element && sortable) sortable.destroy()
})
const _element = unref(element)
if (_element) initSortable(_element)
})

3
packages/nc-gui-v2/components/cell/attachment/utils.ts

@ -31,6 +31,8 @@ export const [useProvideAttachmentCell, useAttachmentCell] = useInjectionState(
const modalVisible = ref(false)
const selectedImage = ref()
const { project } = useProject()
const { api, isLoading } = useApi()
@ -140,6 +142,7 @@ export const [useProvideAttachmentCell, useAttachmentCell] = useInjectionState(
removeFile,
downloadFile,
updateModelValue,
selectedImage,
}
},
'attachmentCell',

Loading…
Cancel
Save