fix: filepicker

This commit is contained in:
Torsten Dittmann
2024-02-26 12:39:43 +01:00
parent f8261e6255
commit d2edd29d1e
5 changed files with 269 additions and 81 deletions
+152 -73
View File
@@ -1,5 +1,5 @@
<script lang="ts">
import { Id, ModalWrapper } from '.';
import { EmptySearch, Id, ModalWrapper, Trim } from '.';
import { Button, Form } from '$lib/elements/forms';
import { sdk } from '$lib/stores/sdk';
import { ID, Query } from '@appwrite.io/console';
@@ -20,6 +20,7 @@
import FormList from '$lib/elements/forms/formList.svelte';
import { writable } from 'svelte/store';
import { onMount } from 'svelte';
import Heading from './heading.svelte';
export let show: boolean;
export let selectedBucket: string = null;
@@ -29,19 +30,20 @@
let search = writable('');
let fileSelector: HTMLInputElement;
let uploading = false;
let view: 'grid' | 'list' = 'list';
onMount(() => {
selectedBucket = currentBucket?.$id;
})
});
function submitForm() {
onSelect(currentFile);
closeModal();
}
function getPreview(bucketId: string, fileId: string) {
function getPreview(bucketId: string, fileId: string, size: number = 64) {
return (
sdk.forProject.storage.getFilePreview(bucketId, fileId, 64, 64).toString() +
sdk.forProject.storage.getFilePreview(bucketId, fileId, size, size).toString() +
'&mode=admin'
);
}
@@ -96,10 +98,7 @@
currentBucket &&
sdk.forProject.storage.listFiles(
currentBucket.$id,
[
Query.startsWith('mimeType', 'image/'),
Query.orderDesc('$createdAt')
],
[Query.startsWith('mimeType', 'image/'), Query.orderDesc('$createdAt')],
$search || undefined
);
@@ -107,9 +106,7 @@
selectedBucket = currentBucket?.$id;
resetFile();
}
$: if (!show) {
closeModal();
}
$: if ($search) {
resetFile();
}
@@ -121,6 +118,7 @@
<div class="u-flex u-main-space-between u-cross-center u-gap-16">
<h4 class="modal-title heading-level-5">Select file</h4>
<button
type="button"
on:click={closeModal}
class="button is-text is-small is-only-icon"
aria-label="Close modal">
@@ -149,6 +147,7 @@
{@const isSelected = bucket.$id === selectedBucket}
<li class="drop-list-item">
<button
type="button"
class="drop-button"
class:is-selected={isSelected}
on:click={() => (currentBucket = bucket)}>
@@ -191,13 +190,19 @@
</div>
<div
class="u-flex u-main-space-between u-gap-16 u-flex-vertical-mobile">
<InputSearch bind:value={$search} />
<InputSearch
placeholder="Search files"
bind:value={$search}
style="min-inline-size: 17.5rem" />
<div class="u-flex u-gap-16">
<div class="toggle-button">
<ul class="toggle-button-list">
<li class="toggle-button-item">
<button
on:click={() => (view = 'list')}
type="button"
class="toggle-button-element"
class:is-selected={view === 'list'}
aria-label="List View">
<span
class="icon-view-list"
@@ -206,7 +211,10 @@
</li>
<li class="toggle-button-item">
<button
class="toggle-button-element is-selected"
on:click={() => (view = 'grid')}
type="button"
class="toggle-button-element"
class:is-selected={view === 'grid'}
aria-label="Grid View">
<span
class="icon-view-grid"
@@ -233,68 +241,139 @@
</div>
</header>
<div class="u-flex-vertical u-stretch">
<TableScroll noMargin transparent dense>
<TableHeader>
<TableCellHead>Filename</TableCellHead>
<TableCellHead width={140} onlyDesktop>Type</TableCellHead>
<TableCellHead width={100} onlyDesktop>Size</TableCellHead>
<TableCellHead width={120} onlyDesktop>Created</TableCellHead>
</TableHeader>
{#if files}
{#await files}
<div
class="u-position-absolute u-width-full-line u-flex u-flex-vertical u-main-center u-cross-center u-gap-16 u-margin-block-start-32"
style="inset-inline-start: 0;">
<div class="loader" />
<p class="text">Loading files...</p>
</div>
{:then response}
<TableBody>
{#each response.files as file}
<TableRowButton on:click={() => selectFile(file)}>
<TableCell title="Filename">
<div
class="u-inline-flex u-cross-center u-gap-12">
{#if files}
{#await files}
<div
class="u-position-absolute u-width-full-line u-flex u-flex-vertical u-main-center u-cross-center u-gap-16 u-margin-block-start-32"
style="inset-inline-start: 0;">
<div class="loader" />
<p class="text">Loading files...</p>
</div>
{:then response}
{#if response.files?.length}
{#if view === 'grid'}
<ul
class="grid-box"
style="--grid-gap:2rem; --grid-item-size:10rem; --grid-item-size-small-screens:8rem;">
{#each response.files as file}
<li>
<label
style:background-image={`url(${getPreview(
currentBucket.$id,
file.$id,
360
)})`}
style:aspect-ratio="1/1"
class="card u-height-100-percent u-flex u-flex-vertical u-gap-16"
style="--card-padding:0.5rem; --card-border-radius:var(--border-radius-large);">
<input
class="u-position-absolute is-small u-margin-block-start-2"
type="radio"
name="file"
value={file.$id}
style:pointer-events="none"
checked={selectedFile ===
file.$id} />
<span class="image">
<img
class="avatar"
style:border-radius="var(--border-radius-xsmall)"
width="28"
height="28"
src={getPreview(
currentBucket.$id,
file.$id
)}
alt={file.name} />
</span>
<span class="text u-trim"
>{file.name}</span>
</div>
</TableCell>
<TableCellText title="Type" onlyDesktop>
{file.mimeType}
</TableCellText>
<TableCellText title="Size" onlyDesktop>
{calculateSize(file.sizeOriginal)}
</TableCellText>
<TableCellText title="Created" onlyDesktop>
{toLocaleDate(file.$createdAt)}
</TableCellText>
</TableRowButton>
{:else}
<p>No files found within this bucket</p>
{/each}
</TableBody>
{/await}
{/if}
</TableScroll>
name="country" />
</label>
</li>
{/each}
</ul>
{/if}
{#if view === 'list'}
<TableScroll noMargin transparent dense>
<TableHeader>
<TableCellHead>Filename</TableCellHead>
<TableCellHead width={140} onlyDesktop
>Type</TableCellHead>
<TableCellHead width={100} onlyDesktop
>Size</TableCellHead>
<TableCellHead width={120} onlyDesktop
>Created</TableCellHead>
</TableHeader>
<TableBody>
{#each response.files as file}
<TableRowButton
on:click={() => selectFile(file)}>
<TableCell title="Filename">
<div
class="u-inline-flex u-cross-center u-gap-12">
<input
type="radio"
name="file"
value={file.$id}
style:pointer-events="none"
checked={selectedFile ===
file.$id} />
<span class="image">
<img
class="avatar"
style:border-radius="var(--border-radius-xsmall)"
width="28"
height="28"
src={getPreview(
currentBucket.$id,
file.$id
)}
alt={file.name} />
</span>
<Trim>
{file.name}
</Trim>
</div>
</TableCell>
<TableCellText title="Type" onlyDesktop>
{file.mimeType}
</TableCellText>
<TableCellText title="Size" onlyDesktop>
{calculateSize(file.sizeOriginal)}
</TableCellText>
<TableCellText
title="Created"
onlyDesktop>
{toLocaleDate(file.$createdAt)}
</TableCellText>
</TableRowButton>
{/each}
</TableBody>
</TableScroll>
{/if}
{:else if $search}
<EmptySearch hidePages hidePagination>
<div class="common-section">
<div class="u-text-center common-section">
<b class="body-text-2 u-bold"
>Sorry we couldn't find "{$search}"</b>
<p>
There are no files that match your search.
</p>
</div>
<div
class="u-flex u-gap-16 common-section u-main-center">
<Button
secondary
on:click={() => ($search = '')}
>Clear search</Button>
</div>
</div>
</EmptySearch>
{:else}
<EmptySearch hidePages hidePagination>
<div class="common-section">
<div class="u-text-center common-section">
<Heading size="7" tag="h2" trimmed={false}>
No files found within this bucket.
</Heading>
<p class="text u-line-height-1-5">
Need a hand? Learn more in our <a
class="link"
href="https://appwrite.io/docs/products/storage"
target="_blank"
rel="noopener noreferrer">
documentation</a
>.
</p>
</div>
</div>
</EmptySearch>
{/if}
{/await}
{/if}
</div>
{/if}
</article>
+2 -3
View File
@@ -90,11 +90,10 @@
block-size: 100%;
min-block-size: 80vh;
@media #{$break2} {
@media #{$break1}, #{$break2} {
min-inline-size: 100%;
min-block-size: 100%;
border-radius: 0;
}
}
</style>
</style>
+1
View File
@@ -31,3 +31,4 @@ export { default as InputDate } from './inputDate.svelte';
export { default as InputDateRange } from './inputDateRange.svelte';
export { default as InputTime } from './inputTime.svelte';
export { default as InputDigits } from './inputDigits.svelte';
export { default as InputFilePicker } from './inputFilePicker.svelte';
@@ -0,0 +1,102 @@
<script lang="ts">
import { Drop, Trim } from '$lib/components';
import FilePicker from '$lib/components/filePicker.svelte';
import { humanFileSize } from '$lib/helpers/sizeConvertion';
import type { Models } from '@appwrite.io/console';
import { Label } from '.';
export let label: string = null;
export let value: Models.File = null;
export let optionalText: string = null;
export let tooltip: string = null;
export let isPopoverDefined = true;
let show = false;
function onSelect(file: Models.File) {
value = file;
}
</script>
<div>
{#if label}
<Label {optionalText} {tooltip} hide={!label}>
{label}{#if $$slots.popover && isPopoverDefined}
<Drop bind:show display="inline-block">
<!-- TODO: make unclicked icon greyed out and hover and clicked filled -->
&nbsp;<button
type="button"
on:click={() => (show = !show)}
class="tooltip"
aria-label="input tooltip">
<span
class="icon-info"
aria-hidden="true"
style="font-size: var(--icon-size-small)" />
</button>
<svelte:fragment slot="list">
<div
class="dropped card u-max-width-250"
style="--p-card-padding: .75rem; box-shadow:var(--shadow-large);">
<slot name="popover" />
</div>
</svelte:fragment>
</Drop>
{/if}
</Label>
{/if}
<div
role="region"
class="box is-no-shadow u-padding-24"
style="--box-border-radius:var(--border-radius-xsmall); z-index: 1">
<div class="upload-file-box">
<div class="upload-file-box-image">
<span class="icon-upload" aria-hidden="true" />
</div>
<div class="u-min-width-0 u-text-center">
<h5 class="upload-file-box-title heading-level-7 u-inline">
<span class="is-only-desktop">Select a file to upload</span>
<span class="is-not-desktop">Select a file to upload</span>
</h5>
</div>
<div class="u-flex u-main-center u-cross-center u-gap-16 u-flex-vertical-mobile">
Max file size: 1MB
<button
class="button is-secondary is-full-width-mobile"
type="button"
on:click={() => (show = true)}>
<span class="text">Browse</span>
</button>
</div>
{#if value}
{@const fileSize = humanFileSize(value.sizeOriginal)}
<ul class="upload-file-box-list u-min-width-0">
<li class="u-flex u-cross-center u-min-width-0">
<span class="icon-document" aria-hidden="true" />
<span class="upload-file-box-name u-min-width-0">
<Trim alternativeTrim>{value.name}</Trim>
</span>
<span
class="upload-file-box-size u-margin-inline-start-4 u-margin-inline-end-16">
{fileSize.value + fileSize.unit}
</span>
<button
on:click={() => (value = null)}
type="button"
class="button is-text is-only-icon u-margin-inline-start-auto"
aria-label="remove file"
style="--button-size:1.5rem;">
<span class="icon-x" aria-hidden="true" />
</button>
</li>
</ul>
{/if}
</div>
</div>
</div>
{#if show}
<FilePicker bind:show {onSelect} />
{/if}
@@ -45,6 +45,7 @@
import { sdk } from '$lib/stores/sdk';
import PushPhone from '../pushPhone.svelte';
import { onMount } from 'svelte';
import InputFilePicker from '$lib/elements/forms/inputFilePicker.svelte';
let showCustomId = false;
let showTest = false;
@@ -148,11 +149,17 @@
</Modal>
</div>
<form class="form">
<FormItem>
<Label
tooltip="A key/value payload of additional metadata that's hidden from users. Use this to include information to support logic such as redirection and routing."
>Custom data <span class="u-color-text-gray">(Optional)</span></Label>
</FormItem>
<FormList>
<FormItem>
<InputFilePicker label="Media" />
</FormItem>
<FormItem>
<Label
tooltip="A key/value payload of additional metadata that's hidden from users. Use this to include information to support logic such as redirection and routing."
>Custom data <span class="u-color-text-gray">(Optional)</span></Label>
</FormItem>
</FormList>
<div class=" u-grid u-gap-8">
<ul class="form-list" style="--p-form-list-gap: 1rem">
{#each customData || [] as _, rowIndex}