FileItem.svelte 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. <script lang="ts">
  2. import { createEventDispatcher, getContext } from 'svelte';
  3. import { formatFileSize } from '$lib/utils';
  4. import FileItemModal from './FileItemModal.svelte';
  5. import GarbageBin from '../icons/GarbageBin.svelte';
  6. import Spinner from './Spinner.svelte';
  7. import Tooltip from './Tooltip.svelte';
  8. import XMark from '$lib/components/icons/XMark.svelte';
  9. import { settings } from '$lib/stores';
  10. const i18n = getContext('i18n');
  11. const dispatch = createEventDispatcher();
  12. export let className = 'w-60';
  13. export let colorClassName = 'bg-white dark:bg-gray-850 border border-gray-50 dark:border-white/5';
  14. export let url: string | null = null;
  15. export let dismissible = false;
  16. export let modal = false;
  17. export let loading = false;
  18. export let item = null;
  19. export let edit = false;
  20. export let small = false;
  21. export let name: string;
  22. export let type: string;
  23. export let size: number;
  24. import { deleteFileById } from '$lib/apis/files';
  25. let showModal = false;
  26. const decodeString = (str: string) => {
  27. try {
  28. return decodeURIComponent(str);
  29. } catch (e) {
  30. return str;
  31. }
  32. };
  33. </script>
  34. {#if item}
  35. <FileItemModal bind:show={showModal} bind:item {edit} />
  36. {/if}
  37. <button
  38. class="relative group p-1.5 {className} flex items-center gap-1 {colorClassName} {small
  39. ? 'rounded-xl'
  40. : 'rounded-2xl'} text-left"
  41. type="button"
  42. on:click={async () => {
  43. if (item?.file?.data?.content || item?.type === 'file' || modal) {
  44. showModal = !showModal;
  45. } else {
  46. if (url) {
  47. if (type === 'file') {
  48. window.open(`${url}/content`, '_blank').focus();
  49. } else {
  50. window.open(`${url}`, '_blank').focus();
  51. }
  52. }
  53. }
  54. dispatch('click');
  55. }}
  56. >
  57. {#if !small}
  58. <div
  59. class="size-10 shrink-0 flex justify-center items-center bg-black/20 dark:bg-white/10 text-white rounded-xl"
  60. >
  61. {#if !loading}
  62. <svg
  63. xmlns="http://www.w3.org/2000/svg"
  64. viewBox="0 0 24 24"
  65. fill="currentColor"
  66. aria-hidden="true"
  67. class=" size-4.5"
  68. >
  69. <path
  70. fill-rule="evenodd"
  71. d="M5.625 1.5c-1.036 0-1.875.84-1.875 1.875v17.25c0 1.035.84 1.875 1.875 1.875h12.75c1.035 0 1.875-.84 1.875-1.875V12.75A3.75 3.75 0 0 0 16.5 9h-1.875a1.875 1.875 0 0 1-1.875-1.875V5.25A3.75 3.75 0 0 0 9 1.5H5.625ZM7.5 15a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 7.5 15Zm.75 2.25a.75.75 0 0 0 0 1.5H12a.75.75 0 0 0 0-1.5H8.25Z"
  72. clip-rule="evenodd"
  73. />
  74. <path
  75. d="M12.971 1.816A5.23 5.23 0 0 1 14.25 5.25v1.875c0 .207.168.375.375.375H16.5a5.23 5.23 0 0 1 3.434 1.279 9.768 9.768 0 0 0-6.963-6.963Z"
  76. />
  77. </svg>
  78. {:else}
  79. <Spinner />
  80. {/if}
  81. </div>
  82. {/if}
  83. {#if !small}
  84. <div class="flex flex-col justify-center -space-y-0.5 px-2.5 w-full">
  85. <div class=" dark:text-gray-100 text-sm font-medium line-clamp-1 mb-1">
  86. {decodeString(name)}
  87. </div>
  88. <div
  89. class=" flex justify-between text-xs line-clamp-1 {($settings?.highContrastMode ?? false)
  90. ? 'text-gray-800 dark:text-gray-100'
  91. : 'text-gray-500'}"
  92. >
  93. {#if type === 'file'}
  94. {$i18n.t('File')}
  95. {:else if type === 'doc'}
  96. {$i18n.t('Document')}
  97. {:else if type === 'collection'}
  98. {$i18n.t('Collection')}
  99. {:else}
  100. <span class=" capitalize line-clamp-1">{type}</span>
  101. {/if}
  102. {#if size}
  103. <span class="capitalize">{formatFileSize(size)}</span>
  104. {/if}
  105. </div>
  106. </div>
  107. {:else}
  108. <Tooltip content={decodeString(name)} className="flex flex-col w-full" placement="top-start">
  109. <div class="flex flex-col justify-center -space-y-0.5 px-2.5 w-full">
  110. <div class=" dark:text-gray-100 text-sm flex justify-between items-center">
  111. {#if loading}
  112. <div class=" shrink-0 mr-2">
  113. <Spinner className="size-4" />
  114. </div>
  115. {/if}
  116. <div class="font-medium line-clamp-1 flex-1">{decodeString(name)}</div>
  117. <div class="text-gray-500 text-xs capitalize shrink-0">{formatFileSize(size)}</div>
  118. </div>
  119. </div>
  120. </Tooltip>
  121. {/if}
  122. {#if dismissible}
  123. <div class=" absolute -top-1 -right-1">
  124. <button
  125. aria-label={$i18n.t('Remove File')}
  126. class=" bg-white text-black border border-gray-50 rounded-full {($settings?.highContrastMode ??
  127. false)
  128. ? ''
  129. : 'outline-hidden focus:outline-hidden group-hover:visible invisible transition'}"
  130. type="button"
  131. on:click|stopPropagation={() => {
  132. dispatch('dismiss');
  133. }}
  134. >
  135. <XMark className={'size-4'} />
  136. </button>
  137. <!-- <button
  138. class=" p-1 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-full group-hover:visible invisible transition"
  139. type="button"
  140. on:click={() => {
  141. }}
  142. >
  143. <GarbageBin />
  144. </button> -->
  145. </div>
  146. {/if}
  147. </button>