mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
fix: filepicker
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 -->
|
||||
<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}
|
||||
|
||||
Reference in New Issue
Block a user