Update to svelte 5
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
<script lang="ts">
|
||||
import { preventDefault } from 'svelte/legacy';
|
||||
import { fs_encode_path, node_is_shared } from "lib/FilesystemAPI";
|
||||
import type { FSNavigator } from "./FSNavigator";
|
||||
|
||||
export let nav: FSNavigator
|
||||
let { nav }: {
|
||||
nav: FSNavigator;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<div class="breadcrumbs">
|
||||
@@ -10,7 +13,7 @@ export let nav: FSNavigator
|
||||
<a
|
||||
href={"/d"+fs_encode_path(node.path)}
|
||||
class="breadcrumb button flat"
|
||||
on:click|preventDefault={() => {nav.navigate(node.path, true)}}
|
||||
onclick={preventDefault(() => {nav.navigate(node.path, true)})}
|
||||
>
|
||||
{#if node.abuse_type !== undefined}
|
||||
<i class="icon small">block</i>
|
||||
|
||||
@@ -1,31 +1,34 @@
|
||||
<script lang="ts">
|
||||
import { run } from 'svelte/legacy';
|
||||
import Chart from "util/Chart.svelte";
|
||||
import { formatDataVolume, formatDate, formatThousands } from "util/Formatting";
|
||||
import Modal from "util/Modal.svelte";
|
||||
import { fs_path_url, fs_share_hotlink_url, fs_share_url, fs_timeseries, type FSNode } from "lib/FilesystemAPI";
|
||||
import { color_by_name } from "util/Util.svelte";
|
||||
import { color_by_name } from "util/Util";
|
||||
import { tick } from "svelte";
|
||||
import CopyButton from "layout/CopyButton.svelte";
|
||||
import type { FSNavigator } from "./FSNavigator";
|
||||
|
||||
export let nav: FSNavigator
|
||||
export let visible = false
|
||||
let {
|
||||
nav,
|
||||
visible = $bindable(false)
|
||||
}: {
|
||||
nav: FSNavigator;
|
||||
visible?: boolean;
|
||||
} = $props();
|
||||
|
||||
export const toggle = () => visible = !visible
|
||||
|
||||
$: visibility_change(visible)
|
||||
const visibility_change = visible => {
|
||||
if (visible) {
|
||||
update_chart(nav.base, 0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
$: direct_url = $nav.base.path ? window.location.origin+fs_path_url($nav.base.path) : ""
|
||||
$: share_url = fs_share_url($nav.path)
|
||||
$: direct_share_url = fs_share_hotlink_url($nav.path)
|
||||
|
||||
let chart
|
||||
let chart_timespan = 0
|
||||
let chart_interval = 0
|
||||
let chart: Chart = $state()
|
||||
let chart_timespan = $state(0)
|
||||
let chart_interval = $state(0)
|
||||
let chart_timespans = [
|
||||
{label: "Day (1m)", span: 1440, interval: 1},
|
||||
{label: "Week (1h)", span: 10080, interval: 60},
|
||||
@@ -36,10 +39,9 @@ let chart_timespans = [
|
||||
{label: "Five Years (1d)", span: 2628000, interval: 1440},
|
||||
]
|
||||
|
||||
let total_downloads = 0
|
||||
let total_transfer = 0
|
||||
let total_downloads = $state(0)
|
||||
let total_transfer = $state(0)
|
||||
|
||||
$: update_chart($nav.base, chart_timespan, chart_interval)
|
||||
let update_chart = async (base: FSNode, timespan: number, interval: number) => {
|
||||
if (chart === undefined) {
|
||||
// Wait for the chart element to render, if it's not rendered already
|
||||
@@ -141,6 +143,15 @@ let update_chart = async (base: FSNode, timespan: number, interval: number) => {
|
||||
console.error("Failed to get time series data:", err)
|
||||
}
|
||||
}
|
||||
run(() => {
|
||||
visibility_change(visible)
|
||||
});
|
||||
let direct_url = $derived($nav.base.path ? window.location.origin+fs_path_url($nav.base.path) : "")
|
||||
let share_url = $derived(fs_share_url($nav.path))
|
||||
let direct_share_url = $derived(fs_share_hotlink_url($nav.path))
|
||||
run(() => {
|
||||
update_chart($nav.base, chart_timespan, chart_interval)
|
||||
});
|
||||
</script>
|
||||
|
||||
<Modal bind:visible={visible} title="Details" width={($nav.base.type === "file" ? 1000 : 750) + "px"}>
|
||||
@@ -231,7 +242,7 @@ let update_chart = async (base: FSNode, timespan: number, interval: number) => {
|
||||
<div class="button_bar">
|
||||
{#each chart_timespans as ts}
|
||||
<button
|
||||
on:click={() => update_chart($nav.base, ts.span, ts.interval)}
|
||||
onclick={() => update_chart($nav.base, ts.span, ts.interval)}
|
||||
class:button_highlight={chart_timespan == ts.span}>
|
||||
{ts.label}
|
||||
</button>
|
||||
|
||||
@@ -4,13 +4,15 @@ import { formatDataVolume, formatThousands } from "util/Formatting"
|
||||
import { fs_path_url } from "lib/FilesystemAPI";
|
||||
import type { FSNavigator } from "./FSNavigator";
|
||||
|
||||
export let nav: FSNavigator
|
||||
let { nav }: {
|
||||
nav: FSNavigator;
|
||||
} = $props();
|
||||
|
||||
let loading = true
|
||||
let downloads = 0
|
||||
let transfer_used = 0
|
||||
let loading = $state(true)
|
||||
let downloads = $state(0)
|
||||
let transfer_used = $state(0)
|
||||
let socket = null
|
||||
let error_msg = ""
|
||||
let error_msg = $state("")
|
||||
|
||||
let connected_to = ""
|
||||
|
||||
@@ -22,9 +24,9 @@ onMount(() => {
|
||||
}
|
||||
})
|
||||
|
||||
let total_directories = 0
|
||||
let total_files = 0
|
||||
let total_file_size = 0
|
||||
let total_directories = $state(0)
|
||||
let total_files = $state(0)
|
||||
let total_file_size = $state(0)
|
||||
|
||||
const update_base = async () => {
|
||||
if (!nav.initialized) {
|
||||
|
||||
@@ -12,14 +12,14 @@ import { css_from_path } from "filesystem/edit_window/Branding";
|
||||
import AffiliatePrompt from "user_home/AffiliatePrompt.svelte";
|
||||
import { current_page_store } from "wrap/RouterStore";
|
||||
|
||||
let file_preview: FilePreview
|
||||
let toolbar: Toolbar
|
||||
let upload_widget: FSUploadWidget
|
||||
let details_visible = false
|
||||
let edit_window: EditWindow
|
||||
let edit_visible = false
|
||||
let file_preview: FilePreview = $state()
|
||||
let toolbar: Toolbar = $state()
|
||||
let upload_widget: FSUploadWidget = $state()
|
||||
let details_visible = $state(false)
|
||||
let edit_window: EditWindow = $state()
|
||||
let edit_visible = $state(false)
|
||||
|
||||
const nav = new FSNavigator(true)
|
||||
const nav = $state(new FSNavigator(true))
|
||||
|
||||
onMount(() => {
|
||||
if ((window as any).intial_node !== undefined) {
|
||||
@@ -134,7 +134,7 @@ const keydown = (e: KeyboardEvent) => {
|
||||
};
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={keydown} />
|
||||
<svelte:window onkeydown={keydown} />
|
||||
|
||||
<div class="filesystem">
|
||||
<Breadcrumbs nav={nav}/>
|
||||
|
||||
@@ -6,23 +6,31 @@ import { formatDataVolume } from "util/Formatting";
|
||||
import { user } from "lib/UserStore";
|
||||
import Dialog from "layout/Dialog.svelte";
|
||||
|
||||
let button: HTMLButtonElement
|
||||
let dialog: Dialog
|
||||
let button: HTMLButtonElement = $state()
|
||||
let dialog: Dialog = $state()
|
||||
|
||||
export let no_login_label = "Pixeldrain"
|
||||
let {
|
||||
no_login_label = "Pixeldrain",
|
||||
hide_name = true,
|
||||
hide_logo = false,
|
||||
style = "",
|
||||
embedded = false
|
||||
}: {
|
||||
no_login_label?: string;
|
||||
// Hide the label if the screen is smaller than 800px
|
||||
hide_name?: boolean;
|
||||
hide_logo?: boolean;
|
||||
style?: string;
|
||||
embedded?: boolean;
|
||||
} = $props();
|
||||
|
||||
// Hide the label if the screen is smaller than 800px
|
||||
export let hide_name = true
|
||||
export let hide_logo = false
|
||||
export let style = ""
|
||||
export let embedded = false
|
||||
$: target = embedded ? "_blank" : "_self"
|
||||
let target = $derived(embedded ? "_blank" : "_self")
|
||||
|
||||
const open = () => dialog.open(button.getBoundingClientRect())
|
||||
</script>
|
||||
|
||||
<div class="wrapper">
|
||||
<button bind:this={button} on:click={open} class="button round" title="Menu" style={style}>
|
||||
<button bind:this={button} onclick={open} class="button round" title="Menu" style={style}>
|
||||
{#if !hide_logo}
|
||||
<PixeldrainLogo style="height: 1.6em; width: 1.6em;"/>
|
||||
{/if}
|
||||
|
||||
@@ -1,22 +1,24 @@
|
||||
<script lang="ts">
|
||||
import type { FSNavigator } from "./FSNavigator";
|
||||
import { fs_node_icon, fs_share_hotlink_url, fs_share_url, fs_update, node_is_shared, type FSNode, type FSPermissions } from "lib/FilesystemAPI";
|
||||
import { copy_text } from "util/Util.svelte";
|
||||
import { copy_text } from "util/Util";
|
||||
import CopyButton from "layout/CopyButton.svelte";
|
||||
import Dialog from "layout/Dialog.svelte";
|
||||
import { fade } from "svelte/transition";
|
||||
|
||||
export let nav: FSNavigator
|
||||
let { nav }: {
|
||||
nav: FSNavigator;
|
||||
} = $props();
|
||||
|
||||
let path: FSNode[]
|
||||
let base: FSNode
|
||||
let toast = ""
|
||||
let share_url = ""
|
||||
let direct_share_url = ""
|
||||
let is_parent = false
|
||||
let parent_node: FSNode
|
||||
let base: FSNode = $state()
|
||||
let toast = $state("")
|
||||
let share_url = $state("")
|
||||
let direct_share_url = $state("")
|
||||
let is_parent = $state(false)
|
||||
let parent_node: FSNode = $state()
|
||||
|
||||
let dialog: Dialog
|
||||
let dialog: Dialog = $state()
|
||||
export const open = async (e: MouseEvent, p: FSNode[]) => {
|
||||
path = p
|
||||
base = path[path.length-1]
|
||||
@@ -113,7 +115,7 @@ const share = async () => {
|
||||
<img src={fs_node_icon(parent_node, 64, 64)} class="node_icon" alt="icon"/>
|
||||
{parent_node.name}
|
||||
<br/>
|
||||
<button on:click={async e => {await make_public(); await share()}} style="display: inline;">
|
||||
<button onclick={async e => {await make_public(); await share()}} style="display: inline;">
|
||||
Only share
|
||||
<img src={fs_node_icon(base, 64, 64)} class="node_icon" alt="icon"/>
|
||||
{base.name}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import { copy_text } from "util/Util.svelte";
|
||||
import { copy_text } from "util/Util";
|
||||
import FileStats from "./FileStats.svelte";
|
||||
import type { FSNavigator } from "./FSNavigator";
|
||||
import EditWindow from "./edit_window/EditWindow.svelte";
|
||||
@@ -10,12 +10,20 @@ import { bookmark_add, bookmark_del, bookmarks_store, is_bookmark } from "lib/Bo
|
||||
|
||||
let dispatch = createEventDispatcher()
|
||||
|
||||
export let nav: FSNavigator
|
||||
export let details_visible = false
|
||||
export let edit_window: EditWindow
|
||||
export let edit_visible = false
|
||||
let share_dialog: ShareDialog
|
||||
let link_copied = false
|
||||
let {
|
||||
nav = $bindable(),
|
||||
details_visible = $bindable(false),
|
||||
edit_window,
|
||||
edit_visible = $bindable(false)
|
||||
}: {
|
||||
nav: FSNavigator;
|
||||
details_visible?: boolean;
|
||||
edit_window: EditWindow;
|
||||
edit_visible?: boolean;
|
||||
} = $props();
|
||||
|
||||
let share_dialog: ShareDialog = $state()
|
||||
let link_copied = $state(false)
|
||||
export const copy_link = () => {
|
||||
const share_url = fs_share_url($nav.path)
|
||||
if (share_url === "") {
|
||||
@@ -34,36 +42,36 @@ export const copy_link = () => {
|
||||
<FileStats nav={nav}/>
|
||||
|
||||
<div class="button_row">
|
||||
<button on:click={() => {nav.open_sibling(-1)}}>
|
||||
<button onclick={() => {nav.open_sibling(-1)}}>
|
||||
<i class="icon">skip_previous</i>
|
||||
</button>
|
||||
<button on:click={() => {nav.shuffle = !nav.shuffle}} class:button_highlight={nav.shuffle}>
|
||||
<button onclick={() => {nav.shuffle = !nav.shuffle}} class:button_highlight={nav.shuffle}>
|
||||
<i class="icon">shuffle</i>
|
||||
</button>
|
||||
<button on:click={() => {nav.open_sibling(1)}}>
|
||||
<button onclick={() => {nav.open_sibling(1)}}>
|
||||
<i class="icon">skip_next</i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button on:click={() => dispatch("download")}>
|
||||
<button onclick={() => dispatch("download")}>
|
||||
<i class="icon">save</i>
|
||||
<span>Download</span>
|
||||
</button>
|
||||
|
||||
{#if is_bookmark($bookmarks_store, $nav.base.id)}
|
||||
<button on:click={() => bookmark_del($nav.base.id)}>
|
||||
<button onclick={() => bookmark_del($nav.base.id)}>
|
||||
<i class="icon">bookmark_remove</i>
|
||||
<span>Bookmark</span>
|
||||
</button>
|
||||
{:else}
|
||||
<button on:click={() => bookmark_add($nav.base)}>
|
||||
<button onclick={() => bookmark_add($nav.base)}>
|
||||
<i class="icon">bookmark_add</i>
|
||||
<span>Bookmark</span>
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
{#if path_is_shared($nav.path)}
|
||||
<button on:click={copy_link} class:button_highlight={link_copied}>
|
||||
<button onclick={copy_link} class:button_highlight={link_copied}>
|
||||
<i class="icon">content_copy</i>
|
||||
<span><u>C</u>opy link</span>
|
||||
</button>
|
||||
@@ -71,19 +79,19 @@ export const copy_link = () => {
|
||||
|
||||
<!-- Share button is enabled when: The browser has a sharing API, or the user can edit the file (to enable sharing)-->
|
||||
{#if navigator.share !== undefined || $nav.permissions.write === true}
|
||||
<button on:click={(e) => share_dialog.open(e, nav.path)}>
|
||||
<button onclick={(e) => share_dialog.open(e, nav.path)}>
|
||||
<i class="icon">share</i>
|
||||
<span>Share</span>
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
<button on:click={() => details_visible = !details_visible} class:button_highlight={details_visible}>
|
||||
<button onclick={() => details_visible = !details_visible} class:button_highlight={details_visible}>
|
||||
<i class="icon">help</i>
|
||||
<span>Deta<u>i</u>ls</span>
|
||||
</button>
|
||||
|
||||
{#if $nav.base.id !== "me" && $nav.permissions.write === true}
|
||||
<button on:click={() => edit_window.edit(nav.base, true, "file")} class:button_highlight={edit_visible}>
|
||||
<button onclick={() => edit_window.edit(nav.base, true, "file")} class:button_highlight={edit_visible}>
|
||||
<i class="icon">edit</i>
|
||||
<span><u>E</u>dit</span>
|
||||
</button>
|
||||
|
||||
@@ -3,10 +3,14 @@ import Button from "layout/Button.svelte";
|
||||
import type { FSPermissions, NodeOptions } from "lib/FilesystemAPI";
|
||||
import PermissionButton from "./PermissionButton.svelte";
|
||||
|
||||
export let options: NodeOptions
|
||||
let {
|
||||
options = $bindable()
|
||||
}: {
|
||||
options: NodeOptions;
|
||||
} = $props();
|
||||
|
||||
let new_user_id = ""
|
||||
let new_user_perms = <FSPermissions>{read: true}
|
||||
let new_user_id = $state("")
|
||||
let new_user_perms = $state(<FSPermissions>{read: true})
|
||||
const add_user = (e: SubmitEvent) => {
|
||||
e.preventDefault()
|
||||
if (options.user_permissions === undefined) {
|
||||
@@ -19,8 +23,8 @@ const del_user = (id: string) => {
|
||||
options.user_permissions = options.user_permissions
|
||||
}
|
||||
|
||||
let new_password = ""
|
||||
let new_password_perms = <FSPermissions>{read: true}
|
||||
let new_password = $state("")
|
||||
let new_password_perms = $state(<FSPermissions>{read: true})
|
||||
const add_password = (e: SubmitEvent) => {
|
||||
e.preventDefault()
|
||||
if (options.password_permissions === undefined) {
|
||||
@@ -64,7 +68,7 @@ const del_password = (pass: string) => {
|
||||
not receive an e-mail invite. Giving write access to a user without giving
|
||||
read access as well does not actually allow them to write anything.
|
||||
</p>
|
||||
<form on:submit={add_user} class="row">
|
||||
<form onsubmit={add_user} class="row">
|
||||
<input type="text" bind:value={new_user_id} placeholder="Username" class="grow" size="1">
|
||||
<Button type="submit" icon="add" label="Add"/>
|
||||
<div class="perms">
|
||||
@@ -94,7 +98,7 @@ const del_password = (pass: string) => {
|
||||
<p>
|
||||
<b>This feature is not implemented currently!</b>
|
||||
</p>
|
||||
<form on:submit={add_password} class="row">
|
||||
<form onsubmit={add_password} class="row">
|
||||
<input type="text" bind:value={new_password} placeholder="Password" class="grow" size="1">
|
||||
<Button type="submit" icon="add" label="Add"/>
|
||||
<div class="perms">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { run } from 'svelte/legacy';
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import ThemePresets from "./ThemePresets.svelte";
|
||||
import { fs_update, fs_node_type, type FSNode, type NodeOptions, node_is_shared, type FSPermissions } from "lib/FilesystemAPI";
|
||||
@@ -7,11 +8,16 @@ import HelpButton from "layout/HelpButton.svelte";
|
||||
import FilePicker from "filesystem/filemanager/FilePicker.svelte";
|
||||
let dispatch = createEventDispatcher()
|
||||
|
||||
export let file: FSNode
|
||||
export let options: NodeOptions
|
||||
export let enabled: boolean
|
||||
let {
|
||||
file = $bindable(),
|
||||
options = $bindable(),
|
||||
enabled = $bindable()
|
||||
}: {
|
||||
file: FSNode;
|
||||
options: NodeOptions;
|
||||
enabled: boolean;
|
||||
} = $props();
|
||||
|
||||
$: update_colors(options)
|
||||
const update_colors = (options: NodeOptions) => {
|
||||
if (enabled) {
|
||||
options.branding_enabled = "true"
|
||||
@@ -21,7 +27,7 @@ const update_colors = (options: NodeOptions) => {
|
||||
}
|
||||
}
|
||||
|
||||
let picker: FilePicker
|
||||
let picker: FilePicker = $state()
|
||||
let picking = ""
|
||||
const pick_image = (type: string) => {
|
||||
picking = type
|
||||
@@ -61,7 +67,10 @@ const handle_picker = async (e: CustomEvent<FSNode[]>) => {
|
||||
}
|
||||
}
|
||||
|
||||
let highlight_info = false
|
||||
let highlight_info = $state(false)
|
||||
run(() => {
|
||||
update_colors(options)
|
||||
});
|
||||
</script>
|
||||
|
||||
<fieldset>
|
||||
@@ -127,7 +136,7 @@ let highlight_info = false
|
||||
working. Recommended dimensions for the header image are 1000x90 px.
|
||||
</p>
|
||||
<div>Header image ID</div>
|
||||
<button on:click={() => pick_image("brand_header_image")}>
|
||||
<button onclick={() => pick_image("brand_header_image")}>
|
||||
<i class="icon">folder_open</i>
|
||||
Pick
|
||||
</button>
|
||||
@@ -135,7 +144,7 @@ let highlight_info = false
|
||||
<div>Header image link</div>
|
||||
<input class="span2" type="text" bind:value={options.brand_header_link}/>
|
||||
<div>Background image ID</div>
|
||||
<button on:click={() => pick_image("brand_background_image")}>
|
||||
<button onclick={() => pick_image("brand_background_image")}>
|
||||
<i class="icon">folder_open</i>
|
||||
Pick
|
||||
</button>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { preventDefault } from 'svelte/legacy';
|
||||
import { fs_rename, fs_update, type FSNode, type NodeOptions } from "lib/FilesystemAPI";
|
||||
import Modal from "util/Modal.svelte";
|
||||
import BrandingOptions from "./BrandingOptions.svelte";
|
||||
@@ -9,13 +10,18 @@ import AccessControl from "./AccessControl.svelte";
|
||||
import type { FSNavigator } from "filesystem/FSNavigator";
|
||||
import { loading_finish, loading_start } from "lib/Loading";
|
||||
|
||||
export let nav: FSNavigator
|
||||
let file: FSNode = {} as FSNode
|
||||
let options: NodeOptions = {} as NodeOptions
|
||||
let file: FSNode = $state({} as FSNode)
|
||||
let options: NodeOptions = $state({} as NodeOptions)
|
||||
|
||||
let custom_css = ""
|
||||
let custom_css = $state("")
|
||||
|
||||
export let visible: boolean
|
||||
let {
|
||||
nav,
|
||||
visible = $bindable()
|
||||
}: {
|
||||
nav: FSNavigator;
|
||||
visible: boolean;
|
||||
} = $props();
|
||||
|
||||
// Open the edit window. Argument 1 is the file to edit, 2 is whether the file
|
||||
// should be opened after the user finishes editing and 3 is the default tab
|
||||
@@ -54,11 +60,11 @@ export const edit = (f: FSNode, oae = false, open_tab = "") => {
|
||||
visible = true
|
||||
}
|
||||
|
||||
let tab = "file"
|
||||
let open_after_edit = false
|
||||
let tab = $state("file")
|
||||
let open_after_edit = $state(false)
|
||||
|
||||
let new_name = ""
|
||||
let branding_enabled = false
|
||||
let new_name = $state("")
|
||||
let branding_enabled = $state(false)
|
||||
|
||||
const save = async (keep_editing = false) => {
|
||||
console.debug("Saving file", file.path)
|
||||
@@ -106,27 +112,27 @@ const save = async (keep_editing = false) => {
|
||||
|
||||
<Modal bind:visible={visible} title="Edit {file.name}" width="800px" form="edit_form" style="color: var(--body_text_color); {custom_css}">
|
||||
<div class="tab_bar">
|
||||
<button class:button_highlight={tab === "file"} on:click={() => tab = "file"}>
|
||||
<button class:button_highlight={tab === "file"} onclick={() => tab = "file"}>
|
||||
<i class="icon">edit</i>
|
||||
Properties
|
||||
</button>
|
||||
<button class:button_highlight={tab === "share"} on:click={() => tab = "share"}>
|
||||
<button class:button_highlight={tab === "share"} onclick={() => tab = "share"}>
|
||||
<i class="icon">share</i>
|
||||
Sharing
|
||||
</button>
|
||||
{#if $nav.permissions.owner}
|
||||
<button class:button_highlight={tab === "access"} on:click={() => tab = "access"}>
|
||||
<button class:button_highlight={tab === "access"} onclick={() => tab = "access"}>
|
||||
<i class="icon">key</i>
|
||||
Access control
|
||||
</button>
|
||||
{/if}
|
||||
<button class:button_highlight={tab === "branding"} on:click={() => tab = "branding"}>
|
||||
<button class:button_highlight={tab === "branding"} onclick={() => tab = "branding"}>
|
||||
<i class="icon">palette</i>
|
||||
Branding
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<form id="edit_form" on:submit|preventDefault={() => save(false)}></form>
|
||||
<form id="edit_form" onsubmit={preventDefault(() => save(false))}></form>
|
||||
|
||||
<div class="tab_content">
|
||||
{#if tab === "file"}
|
||||
@@ -138,7 +144,10 @@ const save = async (keep_editing = false) => {
|
||||
bind:open_after_edit
|
||||
/>
|
||||
{:else if tab === "share"}
|
||||
<SharingOptions bind:file bind:options on:save={() => save(true)} />
|
||||
<SharingOptions
|
||||
bind:file
|
||||
bind:options
|
||||
/>
|
||||
{:else if tab === "access"}
|
||||
<AccessControl bind:options />
|
||||
{:else if tab === "branding"}
|
||||
|
||||
@@ -5,13 +5,21 @@ import PathLink from "filesystem/util/PathLink.svelte";
|
||||
import type { FSNavigator } from "filesystem/FSNavigator";
|
||||
import { loading_finish, loading_start } from "lib/Loading";
|
||||
|
||||
export let nav: FSNavigator
|
||||
export let file: FSNode = {} as FSNode
|
||||
export let new_name: string
|
||||
export let visible: boolean
|
||||
export let open_after_edit: boolean
|
||||
let {
|
||||
nav,
|
||||
file = $bindable({} as FSNode),
|
||||
new_name = $bindable(),
|
||||
visible = $bindable(),
|
||||
open_after_edit = $bindable(false)
|
||||
}: {
|
||||
nav: FSNavigator;
|
||||
file?: FSNode;
|
||||
new_name: string;
|
||||
visible: boolean;
|
||||
open_after_edit: boolean;
|
||||
} = $props();
|
||||
|
||||
$: is_root_dir = file.path === "/"+file.id
|
||||
let is_root_dir = $derived(file.path === "/"+file.id)
|
||||
|
||||
const delete_file = async (e: MouseEvent) => {
|
||||
e.preventDefault()
|
||||
|
||||
@@ -2,9 +2,15 @@
|
||||
import ToggleButton from "layout/ToggleButton.svelte";
|
||||
import type { FSPermissions } from "lib/FilesystemAPI";
|
||||
|
||||
export let permissions = <FSPermissions>{}
|
||||
let {
|
||||
permissions = $bindable()
|
||||
}: {
|
||||
permissions: FSPermissions
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<ToggleButton group_first bind:on={permissions.read}>Read</ToggleButton>
|
||||
<ToggleButton group_middle bind:on={permissions.write}>Write</ToggleButton>
|
||||
<ToggleButton group_last bind:on={permissions.delete}>Delete</ToggleButton>
|
||||
{#if permissions !== undefined}
|
||||
<ToggleButton group_first bind:on={permissions.read}>Read</ToggleButton>
|
||||
<ToggleButton group_middle bind:on={permissions.write}>Write</ToggleButton>
|
||||
<ToggleButton group_last bind:on={permissions.delete}>Delete</ToggleButton>
|
||||
{/if}
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
<script lang="ts">
|
||||
import { domain_url } from "util/Util.svelte";
|
||||
import { run } from 'svelte/legacy';
|
||||
import { domain_url } from "util/Util";
|
||||
import CopyButton from "layout/CopyButton.svelte";
|
||||
import { formatDate } from "util/Formatting";
|
||||
import { node_is_shared, type FSNode, type NodeOptions } from "lib/FilesystemAPI";
|
||||
import AccessControl from "./AccessControl.svelte";
|
||||
|
||||
export let file: FSNode = {} as FSNode
|
||||
export let options: NodeOptions
|
||||
let {
|
||||
file = $bindable(),
|
||||
options = $bindable(),
|
||||
}: {
|
||||
file?: FSNode;
|
||||
options: NodeOptions;
|
||||
} = $props();
|
||||
|
||||
let embed_html: string
|
||||
let preview_area: HTMLDivElement
|
||||
let embed_html: string = $state()
|
||||
let preview_area: HTMLDivElement = $state()
|
||||
|
||||
$: share_link = window.location.protocol+"//"+window.location.host+"/d/"+file.id
|
||||
$: embed_iframe(file, options)
|
||||
const embed_iframe = (file: FSNode, options: NodeOptions) => {
|
||||
if (!node_is_shared(file)) {
|
||||
example = false
|
||||
@@ -28,7 +32,7 @@ const embed_iframe = (file: FSNode, options: NodeOptions) => {
|
||||
`></iframe>`
|
||||
}
|
||||
|
||||
let example = false
|
||||
let example = $state(false)
|
||||
const toggle_example = () => {
|
||||
if (node_is_shared(file)) {
|
||||
example = !example
|
||||
@@ -40,6 +44,10 @@ const toggle_example = () => {
|
||||
}
|
||||
}
|
||||
|
||||
let share_link = $derived(window.location.protocol+"//"+window.location.host+"/d/"+file.id)
|
||||
run(() => {
|
||||
embed_iframe(file, options)
|
||||
});
|
||||
</script>
|
||||
|
||||
<fieldset>
|
||||
@@ -78,7 +86,7 @@ const toggle_example = () => {
|
||||
<textarea bind:value={embed_html} style="width: 100%; height: 4em;"></textarea>
|
||||
<br/>
|
||||
<CopyButton text={embed_html}>Copy HTML</CopyButton>
|
||||
<button on:click={toggle_example} class:button_highlight={example} disabled={!node_is_shared(file)}>
|
||||
<button onclick={toggle_example} class:button_highlight={example} disabled={!node_is_shared(file)}>
|
||||
<i class="icon">visibility</i> Show example
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
<script lang="ts">
|
||||
import type { FSNodeProperties } from "lib/FilesystemAPI";
|
||||
|
||||
export let properties: FSNodeProperties = {} as FSNodeProperties
|
||||
let {
|
||||
properties = $bindable({} as FSNodeProperties)
|
||||
}: {
|
||||
properties?: FSNodeProperties;
|
||||
} = $props();
|
||||
|
||||
let current_theme = -1
|
||||
let current_theme = $state(-1)
|
||||
|
||||
const set_theme = (index: number) => {
|
||||
current_theme = index
|
||||
@@ -71,7 +75,7 @@ const themes = [
|
||||
</script>
|
||||
|
||||
{#each themes as theme, index (theme.name)}
|
||||
<button class:button_highlight={current_theme === index} on:click={() => {set_theme(index)}}>
|
||||
<button class:button_highlight={current_theme === index} onclick={() => {set_theme(index)}}>
|
||||
{theme.name}
|
||||
</button>
|
||||
{/each}
|
||||
|
||||
@@ -6,18 +6,25 @@ import { FileAction } from "./FileManagerLib";
|
||||
|
||||
let dispatch = createEventDispatcher()
|
||||
|
||||
export let nav: FSNavigator
|
||||
export let show_hidden = false
|
||||
export let large_icons = false
|
||||
export let hide_edit = false
|
||||
let {
|
||||
nav,
|
||||
show_hidden = false,
|
||||
large_icons = false,
|
||||
hide_edit = false
|
||||
}: {
|
||||
nav: FSNavigator;
|
||||
show_hidden?: boolean;
|
||||
large_icons?: boolean;
|
||||
hide_edit?: boolean;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<div class="directory">
|
||||
{#each $nav.children as child, index (child.path)}
|
||||
<a
|
||||
href={"/d"+fs_encode_path(child.path)}
|
||||
on:click={e => dispatch("file", {index: index, action: FileAction.Click, original: e})}
|
||||
on:contextmenu={e => dispatch("file", {index: index, action: FileAction.Context, original: e})}
|
||||
onclick={e => dispatch("file", {index: index, action: FileAction.Click, original: e})}
|
||||
oncontextmenu={e => dispatch("file", {index: index, action: FileAction.Context, original: e})}
|
||||
class="node"
|
||||
class:node_selected={child.fm_selected}
|
||||
class:hidden={child.name.startsWith(".") && !show_hidden}
|
||||
@@ -26,20 +33,15 @@ export let hide_edit = false
|
||||
<div class="node_name">
|
||||
{child.name}
|
||||
</div>
|
||||
|
||||
{#if node_is_shared(child)}
|
||||
<a
|
||||
href="/d/{child.id}"
|
||||
on:click={e => dispatch("file", {index: index, action: FileAction.Share, original: e})}
|
||||
class="button flat action_button"
|
||||
>
|
||||
<i class="icon" title="This file / directory is shared. Click to open public link">share</i>
|
||||
</a>
|
||||
<i class="icon" title="This file / directory is shared. Click to open public link">share</i>
|
||||
{/if}
|
||||
|
||||
{#if !hide_edit}
|
||||
<button
|
||||
class="action_button flat"
|
||||
on:click={e => dispatch("file", {index: index, action: FileAction.Menu, original: e})}
|
||||
onclick={e => dispatch("file", {index: index, action: FileAction.Menu, original: e})}
|
||||
>
|
||||
<i class="icon">menu</i>
|
||||
</button>
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
<script lang="ts">
|
||||
import { preventDefault } from 'svelte/legacy';
|
||||
import { onMount } from "svelte";
|
||||
import { fs_mkdir } from "lib/FilesystemAPI";
|
||||
import Button from "layout/Button.svelte";
|
||||
import type { FSNavigator } from "filesystem/FSNavigator";
|
||||
import { loading_finish, loading_start } from "lib/Loading";
|
||||
|
||||
export let nav: FSNavigator
|
||||
let { nav }: { nav: FSNavigator } = $props();
|
||||
|
||||
let name_input: HTMLInputElement;
|
||||
let new_dir_name = ""
|
||||
let error_msg = ""
|
||||
let name_input: HTMLInputElement = $state();
|
||||
let new_dir_name = $state("")
|
||||
let error_msg = $state("")
|
||||
let create_dir = async () => {
|
||||
let form = new FormData()
|
||||
form.append("type", "dir")
|
||||
@@ -42,7 +43,7 @@ onMount(() => {
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<form id="create_dir_form" class="create_dir" on:submit|preventDefault={create_dir}>
|
||||
<form id="create_dir_form" class="create_dir" onsubmit={preventDefault(create_dir)}>
|
||||
<img src="/res/img/mime/folder.png" class="icon" alt="icon"/>
|
||||
<input class="dirname" type="text" bind:this={name_input} bind:value={new_dir_name} />
|
||||
<Button form="create_dir_form" type="submit" icon="create_new_folder" label="Create"/>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { run } from 'svelte/legacy';
|
||||
import { fs_delete_all, fs_download, fs_rename, type FSNode } from "lib/FilesystemAPI"
|
||||
import { onMount } from "svelte"
|
||||
import CreateDirectory from "./CreateDirectory.svelte"
|
||||
@@ -16,19 +17,29 @@ import { FileAction, type FileEvent } from "./FileManagerLib";
|
||||
import FileMenu from "./FileMenu.svelte";
|
||||
import { loading_finish, loading_start } from "lib/Loading";
|
||||
|
||||
export let nav: FSNavigator
|
||||
export let upload_widget: FsUploadWidget
|
||||
export let edit_window: EditWindow
|
||||
export let directory_view = ""
|
||||
let large_icons = false
|
||||
let {
|
||||
nav = $bindable(),
|
||||
upload_widget,
|
||||
edit_window = $bindable(),
|
||||
directory_view = $bindable(""),
|
||||
children
|
||||
}: {
|
||||
nav: FSNavigator;
|
||||
upload_widget: FsUploadWidget;
|
||||
edit_window: EditWindow;
|
||||
directory_view?: string;
|
||||
children?: import('svelte').Snippet;
|
||||
} = $props();
|
||||
|
||||
let large_icons = $state(false)
|
||||
let uploader: FsUploadWidget
|
||||
let mode = "viewing"
|
||||
let creating_dir = false
|
||||
let show_hidden = false
|
||||
let file_menu: FileMenu
|
||||
let mode = $state("viewing")
|
||||
let creating_dir = $state(false)
|
||||
let show_hidden = $state(false)
|
||||
let file_menu: FileMenu = $state()
|
||||
|
||||
export const upload = (files: File[]) => {
|
||||
return uploader.upload(files)
|
||||
return uploader.upload_files(files)
|
||||
}
|
||||
|
||||
// Navigation functions
|
||||
@@ -228,10 +239,6 @@ const select_node = (index: number) => {
|
||||
last_selected_node = index
|
||||
}
|
||||
|
||||
// When the directory is reloaded we want to keep our selection, so this
|
||||
// function watches the children array for changes and updates the selection
|
||||
// when it changes
|
||||
$: update($nav.children)
|
||||
const update = (children: FSNode[]) => {
|
||||
creating_dir = false
|
||||
|
||||
@@ -245,8 +252,8 @@ const update = (children: FSNode[]) => {
|
||||
}
|
||||
}
|
||||
|
||||
let moving_files = 0
|
||||
let moving_directories = 0
|
||||
let moving_files = $state(0)
|
||||
let moving_directories = $state(0)
|
||||
const move_start = () => {
|
||||
moving_files = 0
|
||||
moving_directories = 0
|
||||
@@ -296,9 +303,15 @@ onMount(() => {
|
||||
directory_view = "list"
|
||||
}
|
||||
})
|
||||
// When the directory is reloaded we want to keep our selection, so this
|
||||
// function watches the children array for changes and updates the selection
|
||||
// when it changes
|
||||
run(() => {
|
||||
update($nav.children)
|
||||
});
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={keypress} on:keyup={keypress} />
|
||||
<svelte:window onkeydown={keypress} onkeyup={keypress} />
|
||||
|
||||
<div
|
||||
class="container"
|
||||
@@ -311,25 +324,25 @@ onMount(() => {
|
||||
{#if mode === "viewing"}
|
||||
<div class="toolbar">
|
||||
<div class="toolbar_left">
|
||||
<button on:click={navigate_back} title="Back">
|
||||
<button onclick={navigate_back} title="Back">
|
||||
<i class="icon">arrow_back</i>
|
||||
</button>
|
||||
<button on:click={() => nav.navigate_up()} disabled={$nav.path.length <= 1} title="Up">
|
||||
<button onclick={() => nav.navigate_up()} disabled={$nav.path.length <= 1} title="Up">
|
||||
<i class="icon">north</i>
|
||||
</button>
|
||||
<button on:click={() => nav.reload()} title="Refresh directory listing">
|
||||
<button onclick={() => nav.reload()} title="Refresh directory listing">
|
||||
<i class="icon">refresh</i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="toolbar_middle">
|
||||
<button on:click={() => toggle_view()} title="Switch between gallery, list and compact view">
|
||||
<button onclick={() => toggle_view()} title="Switch between gallery, list and compact view">
|
||||
<i class="icon" class:button_highlight={directory_view === "list"}>list</i>
|
||||
<i class="icon" class:button_highlight={directory_view === "gallery"}>collections</i>
|
||||
<i class="icon" class:button_highlight={directory_view === "compact"}>view_compact</i>
|
||||
</button>
|
||||
|
||||
<button class="button_large_icons" on:click={() => toggle_large_icons()} title="Switch between large and small icons">
|
||||
<button class="button_large_icons" onclick={() => toggle_large_icons()} title="Switch between large and small icons">
|
||||
{#if large_icons}
|
||||
<i class="icon">zoom_out</i>
|
||||
{:else}
|
||||
@@ -337,7 +350,7 @@ onMount(() => {
|
||||
{/if}
|
||||
</button>
|
||||
|
||||
<button on:click={() => {show_hidden = !show_hidden}} title="Toggle hidden files">
|
||||
<button onclick={() => {show_hidden = !show_hidden}} title="Toggle hidden files">
|
||||
{#if show_hidden}
|
||||
<i class="icon">visibility_off</i>
|
||||
{:else}
|
||||
@@ -348,13 +361,13 @@ onMount(() => {
|
||||
|
||||
<div class="toolbar_right">
|
||||
{#if $nav.permissions.write}
|
||||
<button on:click={() => upload_widget.pick_files()} title="Upload files to this directory">
|
||||
<button onclick={() => upload_widget.pick_files()} title="Upload files to this directory">
|
||||
<i class="icon">cloud_upload</i>
|
||||
</button>
|
||||
|
||||
<Button click={() => {creating_dir = !creating_dir}} highlight={creating_dir} icon="create_new_folder" title="Make folder"/>
|
||||
|
||||
<button on:click={selecting_mode} title="Select and delete files">
|
||||
<button onclick={selecting_mode} title="Select and delete files">
|
||||
<i class="icon">select_all</i>
|
||||
</button>
|
||||
{/if}
|
||||
@@ -368,7 +381,7 @@ onMount(() => {
|
||||
<Button click={viewing_mode} icon="close"/>
|
||||
<div class="toolbar_spacer">Selecting files</div>
|
||||
<Button click={move_start} icon="drive_file_move" label="Move"/>
|
||||
<button on:click={delete_selected} class="button_red">
|
||||
<button onclick={delete_selected} class="button_red">
|
||||
<i class="icon">delete</i>
|
||||
Delete
|
||||
</button>
|
||||
@@ -407,7 +420,7 @@ onMount(() => {
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<slot></slot>
|
||||
{@render children?.()}
|
||||
|
||||
{#if directory_view === "list"}
|
||||
<ListView nav={nav} show_hidden={show_hidden} large_icons={large_icons} on:file={file_event} />
|
||||
@@ -418,7 +431,7 @@ onMount(() => {
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<FileMenu bind:this={file_menu} bind:nav bind:edit_window />
|
||||
<FileMenu bind:this={file_menu} nav={nav} edit_window={edit_window} />
|
||||
|
||||
<style>
|
||||
.container {
|
||||
|
||||
@@ -7,10 +7,16 @@ import { bookmark_add, bookmark_del, bookmarks_store, is_bookmark } from "lib/Bo
|
||||
import { fs_download, type FSNode } from "lib/FilesystemAPI";
|
||||
import { tick } from "svelte";
|
||||
|
||||
export let nav: FSNavigator
|
||||
export let edit_window: EditWindow
|
||||
let dialog: Dialog
|
||||
let node: FSNode = null
|
||||
let {
|
||||
nav,
|
||||
edit_window
|
||||
}: {
|
||||
nav: FSNavigator;
|
||||
edit_window: EditWindow;
|
||||
} = $props();
|
||||
|
||||
let dialog: Dialog = $state()
|
||||
let node: FSNode = $state(null)
|
||||
|
||||
export const open = async (n: FSNode, target: EventTarget) => {
|
||||
node = n
|
||||
|
||||
@@ -9,25 +9,28 @@ import { FSNavigator } from "filesystem/FSNavigator";
|
||||
import type { FSNode } from "lib/FilesystemAPI";
|
||||
import { FileAction, type FileEvent } from "./FileManagerLib";
|
||||
|
||||
let nav = new FSNavigator(false)
|
||||
let modal: Modal
|
||||
let nav = $state(new FSNavigator(false))
|
||||
let modal: Modal = $state()
|
||||
let dispatch = createEventDispatcher()
|
||||
let directory_view = ""
|
||||
let large_icons = false
|
||||
let show_hidden = false
|
||||
export let select_multiple = false
|
||||
let directory_view = $state("")
|
||||
let large_icons = $state(false)
|
||||
let show_hidden = $state(false)
|
||||
|
||||
let { select_multiple = false }: {
|
||||
select_multiple?: boolean;
|
||||
} = $props();
|
||||
|
||||
export const open = (path: string) => {
|
||||
modal.show()
|
||||
nav.navigate(path, false)
|
||||
}
|
||||
|
||||
$: selected_files = $nav.children.reduce((acc, file) => {
|
||||
let selected_files = $derived($nav.children.reduce((acc, file) => {
|
||||
if (file.fm_selected) {
|
||||
acc++
|
||||
}
|
||||
return acc
|
||||
}, 0)
|
||||
}, 0))
|
||||
|
||||
// Navigation functions
|
||||
|
||||
@@ -128,42 +131,44 @@ onMount(() => {
|
||||
})
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={detect_shift} on:keyup={detect_shift} />
|
||||
<svelte:window onkeydown={detect_shift} onkeyup={detect_shift} />
|
||||
|
||||
<Modal bind:this={modal} width="900px">
|
||||
<div class="header" slot="title">
|
||||
<button class="button round" on:click={modal.hide}>
|
||||
<i class="icon">close</i>
|
||||
</button>
|
||||
<button on:click={() => nav.navigate_up()} disabled={$nav.path.length <= 1} title="Up">
|
||||
<i class="icon">north</i>
|
||||
</button>
|
||||
<button on:click={() => nav.reload()} title="Refresh directory listing">
|
||||
<i class="icon">refresh</i>
|
||||
</button>
|
||||
{#snippet title()}
|
||||
<div class="header" >
|
||||
<button class="button round" onclick={modal.hide}>
|
||||
<i class="icon">close</i>
|
||||
</button>
|
||||
<button onclick={() => nav.navigate_up()} disabled={$nav.path.length <= 1} title="Up">
|
||||
<i class="icon">north</i>
|
||||
</button>
|
||||
<button onclick={() => nav.reload()} title="Refresh directory listing">
|
||||
<i class="icon">refresh</i>
|
||||
</button>
|
||||
|
||||
<div class="title">
|
||||
Selected {selected_files} files
|
||||
<div class="title">
|
||||
Selected {selected_files} files
|
||||
</div>
|
||||
|
||||
<button onclick={() => {show_hidden = !show_hidden}} title="Toggle hidden files">
|
||||
{#if show_hidden}
|
||||
<i class="icon">visibility_off</i>
|
||||
{:else}
|
||||
<i class="icon">visibility</i>
|
||||
{/if}
|
||||
</button>
|
||||
|
||||
<button onclick={() => toggle_view()} title="Switch between gallery, list and compact view">
|
||||
<i class="icon" class:button_highlight={directory_view === "list"}>list</i>
|
||||
<i class="icon" class:button_highlight={directory_view === "gallery"}>collections</i>
|
||||
<i class="icon" class:button_highlight={directory_view === "compact"}>view_compact</i>
|
||||
</button>
|
||||
|
||||
<button class="button button_highlight round" onclick={done}>
|
||||
<i class="icon">done</i> Pick
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button on:click={() => {show_hidden = !show_hidden}} title="Toggle hidden files">
|
||||
{#if show_hidden}
|
||||
<i class="icon">visibility_off</i>
|
||||
{:else}
|
||||
<i class="icon">visibility</i>
|
||||
{/if}
|
||||
</button>
|
||||
|
||||
<button on:click={() => toggle_view()} title="Switch between gallery, list and compact view">
|
||||
<i class="icon" class:button_highlight={directory_view === "list"}>list</i>
|
||||
<i class="icon" class:button_highlight={directory_view === "gallery"}>collections</i>
|
||||
<i class="icon" class:button_highlight={directory_view === "compact"}>view_compact</i>
|
||||
</button>
|
||||
|
||||
<button class="button button_highlight round" on:click={done}>
|
||||
<i class="icon">done</i> Pick
|
||||
</button>
|
||||
</div>
|
||||
{/snippet}
|
||||
|
||||
<Breadcrumbs nav={nav}/>
|
||||
|
||||
|
||||
@@ -5,17 +5,23 @@ import type { FSNavigator } from "filesystem/FSNavigator";
|
||||
import { FileAction } from "./FileManagerLib";
|
||||
let dispatch = createEventDispatcher()
|
||||
|
||||
export let nav: FSNavigator
|
||||
export let show_hidden = false
|
||||
export let large_icons = false
|
||||
let {
|
||||
nav,
|
||||
show_hidden = false,
|
||||
large_icons = false
|
||||
}: {
|
||||
nav: FSNavigator;
|
||||
show_hidden?: boolean;
|
||||
large_icons?: boolean;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<div class="gallery">
|
||||
{#each $nav.children as child, index (child.path)}
|
||||
<a class="file"
|
||||
href={"/d"+fs_encode_path(child.path)}
|
||||
on:click={e => dispatch("file", {index: index, action: FileAction.Click, original: e})}
|
||||
on:contextmenu={e => dispatch("file", {index: index, action: FileAction.Context, original: e})}
|
||||
onclick={e => dispatch("file", {index: index, action: FileAction.Click, original: e})}
|
||||
oncontextmenu={e => dispatch("file", {index: index, action: FileAction.Context, original: e})}
|
||||
class:selected={child.fm_selected}
|
||||
class:hidden={child.name.startsWith(".") && !show_hidden}
|
||||
class:large_icons
|
||||
|
||||
@@ -8,68 +8,81 @@ import { FileAction } from "./FileManagerLib";
|
||||
|
||||
let dispatch = createEventDispatcher()
|
||||
|
||||
export let nav: FSNavigator
|
||||
export let show_hidden = false
|
||||
export let large_icons = false
|
||||
export let hide_edit = false
|
||||
export let hide_branding = false
|
||||
let {
|
||||
nav,
|
||||
show_hidden = false,
|
||||
large_icons = false,
|
||||
hide_edit = false,
|
||||
hide_branding = false
|
||||
}: {
|
||||
nav: FSNavigator;
|
||||
show_hidden?: boolean;
|
||||
large_icons?: boolean;
|
||||
hide_edit?: boolean;
|
||||
hide_branding?: boolean;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<div class="directory">
|
||||
<tr>
|
||||
<td></td>
|
||||
<td><SortButton active_field={$nav.sort_last_field} asc={$nav.sort_asc} sort_func={nav.sort_children} field="name">Name</SortButton></td>
|
||||
<td class="hide_small"><SortButton active_field={$nav.sort_last_field} asc={$nav.sort_asc} sort_func={nav.sort_children} field="file_size">Size</SortButton></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
{#each $nav.children as child, index (child.path)}
|
||||
<a
|
||||
href={"/d"+fs_encode_path(child.path)}
|
||||
on:click={e => dispatch("file", {index: index, action: FileAction.Click, original: e})}
|
||||
on:contextmenu={e => dispatch("file", {index: index, action: FileAction.Context, original: e})}
|
||||
class="node"
|
||||
class:node_selected={child.fm_selected}
|
||||
class:hidden={child.name.startsWith(".") && !show_hidden}
|
||||
>
|
||||
<td>
|
||||
<img src={fs_node_icon(child, 64, 64)} class="node_icon" class:large_icons alt="icon"/>
|
||||
</td>
|
||||
<td class="node_name">
|
||||
{child.name}
|
||||
</td>
|
||||
<td class="node_size hide_small">
|
||||
{#if child.type === "file"}
|
||||
{formatDataVolume(child.file_size, 3)}
|
||||
{/if}
|
||||
</td>
|
||||
<td class="node_icons">
|
||||
<div class="icons_wrap">
|
||||
{#if child.abuse_type !== undefined}
|
||||
<i class="icon" title="This file / directory has received an abuse report. It cannot be shared">block</i>
|
||||
{:else if node_is_shared(child)}
|
||||
<a
|
||||
href="/d/{child.id}"
|
||||
on:click={e => dispatch("file", {index: index, action: FileAction.Share, original: e})}
|
||||
class="button action_button"
|
||||
>
|
||||
<i class="icon" title="This file / directory is shared. Click to open public link">share</i>
|
||||
</a>
|
||||
<table class="directory">
|
||||
<thead>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td><SortButton active_field={$nav.sort_last_field} asc={$nav.sort_asc} sort_func={nav.sort_children} field="name">Name</SortButton></td>
|
||||
<td class="hide_small"><SortButton active_field={$nav.sort_last_field} asc={$nav.sort_asc} sort_func={nav.sort_children} field="file_size">Size</SortButton></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each $nav.children as child, index (child.path)}
|
||||
<tr
|
||||
onclick={e => dispatch("file", {index: index, action: FileAction.Click, original: e})}
|
||||
oncontextmenu={e => dispatch("file", {index: index, action: FileAction.Context, original: e})}
|
||||
class="node"
|
||||
class:node_selected={child.fm_selected}
|
||||
class:hidden={child.name.startsWith(".") && !show_hidden}
|
||||
>
|
||||
<td>
|
||||
<img src={fs_node_icon(child, 64, 64)} class="node_icon" class:large_icons alt="icon"/>
|
||||
</td>
|
||||
<td class="node_name">
|
||||
<a href={"/d"+fs_encode_path(child.path)}>
|
||||
{child.name}
|
||||
</a>
|
||||
</td>
|
||||
<td class="node_size hide_small">
|
||||
{#if child.type === "file"}
|
||||
{formatDataVolume(child.file_size, 3)}
|
||||
{/if}
|
||||
{#if child.properties !== undefined && child.properties.branding_enabled === "true" && !hide_branding}
|
||||
<button class="action_button" on:click={e => dispatch("file", {index: index, action: FileAction.Branding, original: e})}>
|
||||
<i class="icon">palette</i>
|
||||
</button>
|
||||
{/if}
|
||||
{#if !hide_edit}
|
||||
<button class="action_button" on:click={e => dispatch("file", {index: index, action: FileAction.Menu, original: e})}>
|
||||
<i class="icon">menu</i>
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
</td>
|
||||
</a>
|
||||
{/each}
|
||||
</div>
|
||||
</td>
|
||||
<td class="node_icons">
|
||||
<div class="icons_wrap">
|
||||
{#if child.abuse_type !== undefined}
|
||||
<i class="icon" title="This file / directory has received an abuse report. It cannot be shared">block</i>
|
||||
{:else if node_is_shared(child)}
|
||||
<a
|
||||
href="/d/{child.id}"
|
||||
onclick={e => dispatch("file", {index: index, action: FileAction.Share, original: e})}
|
||||
class="button action_button"
|
||||
>
|
||||
<i class="icon" title="This file / directory is shared. Click to open public link">share</i>
|
||||
</a>
|
||||
{/if}
|
||||
{#if child.properties !== undefined && child.properties.branding_enabled === "true" && !hide_branding}
|
||||
<button class="action_button" onclick={e => dispatch("file", {index: index, action: FileAction.Branding, original: e})}>
|
||||
<i class="icon">palette</i>
|
||||
</button>
|
||||
{/if}
|
||||
{#if !hide_edit}
|
||||
<button class="action_button" onclick={e => dispatch("file", {index: index, action: FileAction.Menu, original: e})}>
|
||||
<i class="icon">menu</i>
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<style>
|
||||
.directory {
|
||||
|
||||
@@ -1,19 +1,23 @@
|
||||
<script lang="ts">
|
||||
import { preventDefault } from 'svelte/legacy';
|
||||
|
||||
import { onMount } from "svelte";
|
||||
import { fs_search, fs_encode_path, fs_thumbnail_url } from "lib/FilesystemAPI";
|
||||
import type { FSNavigator } from "filesystem/FSNavigator";
|
||||
import { loading_finish, loading_start } from "lib/Loading";
|
||||
import { loading_finish, loading_start } from "lib/Loading";
|
||||
|
||||
export let nav: FSNavigator
|
||||
let { nav }: {
|
||||
nav: FSNavigator;
|
||||
} = $props();
|
||||
|
||||
let search_bar: HTMLInputElement
|
||||
let error = ""
|
||||
let search_term = ""
|
||||
let search_results: string[] = []
|
||||
let selected_result = 0
|
||||
let search_bar: HTMLInputElement = $state()
|
||||
let error = $state("")
|
||||
let search_term = $state("")
|
||||
let search_results: string[] = $state([])
|
||||
let selected_result = $state(0)
|
||||
let searching = false
|
||||
let last_searched_term = ""
|
||||
let last_limit = 10
|
||||
let last_limit = $state(10)
|
||||
|
||||
onMount(() => {
|
||||
// Clear results when the user moves to a new directory
|
||||
@@ -144,7 +148,7 @@ const window_keydown = (e: KeyboardEvent) => {
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={window_keydown} />
|
||||
<svelte:window onkeydown={window_keydown} />
|
||||
|
||||
{#if error === "path_not_found" || error === "node_is_a_directory"}
|
||||
<div class="highlight_yellow center">
|
||||
@@ -160,7 +164,7 @@ const window_keydown = (e: KeyboardEvent) => {
|
||||
{/if}
|
||||
|
||||
<div class="center">
|
||||
<form class="search_form" on:submit|preventDefault={submit_search}>
|
||||
<form class="search_form" onsubmit={preventDefault(submit_search)}>
|
||||
<i class="icon">search</i>
|
||||
<input
|
||||
bind:this={search_bar}
|
||||
@@ -169,12 +173,12 @@ const window_keydown = (e: KeyboardEvent) => {
|
||||
placeholder="Press / to search in {$nav.base.name}"
|
||||
style="width: 100%;"
|
||||
bind:value={search_term}
|
||||
on:keydown={input_keydown}
|
||||
on:keyup={input_keyup}
|
||||
onkeydown={input_keydown}
|
||||
onkeyup={input_keyup}
|
||||
/>
|
||||
{#if search_term !== ""}
|
||||
<!-- Button needs to be of button type in order to not submit the form -->
|
||||
<button on:click={() => clear_search(false)} type="button">
|
||||
<button onclick={() => clear_search(false)} type="button">
|
||||
<i class="icon">close</i>
|
||||
</button>
|
||||
{/if}
|
||||
@@ -188,7 +192,7 @@ const window_keydown = (e: KeyboardEvent) => {
|
||||
{#each search_results as result, index}
|
||||
<a
|
||||
href={"/d"+fs_encode_path(result)}
|
||||
on:click|preventDefault={() => open_result(index)}
|
||||
onclick={preventDefault(() => open_result(index))}
|
||||
class="node"
|
||||
class:node_selected={selected_result === index}
|
||||
>
|
||||
@@ -203,7 +207,7 @@ const window_keydown = (e: KeyboardEvent) => {
|
||||
{#if search_results.length === last_limit}
|
||||
<div class="node">
|
||||
<div class="node_name" style="text-align: center;">
|
||||
<button on:click={() => {search(last_limit + 100)}}>
|
||||
<button onclick={() => {search(last_limit + 100)}}>
|
||||
<i class="icon">expand_more</i>
|
||||
More results
|
||||
</button>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<script lang="ts" context="module">
|
||||
<script lang="ts" module>
|
||||
export type UploadJob = {
|
||||
task_id: number,
|
||||
file: File,
|
||||
@@ -14,9 +14,11 @@ import { tick } from "svelte";
|
||||
import UploadProgress from "./UploadProgress.svelte";
|
||||
import type { FSNavigator } from "filesystem/FSNavigator";
|
||||
|
||||
export let nav: FSNavigator
|
||||
let { nav }: {
|
||||
nav: FSNavigator;
|
||||
} = $props();
|
||||
|
||||
let file_input_field: HTMLInputElement
|
||||
let file_input_field: HTMLInputElement = $state()
|
||||
let file_input_change = (e: Event) => {
|
||||
// Start uploading the files async
|
||||
upload_files((e.target as HTMLInputElement).files)
|
||||
@@ -28,8 +30,8 @@ export const pick_files = () => {
|
||||
file_input_field.click()
|
||||
}
|
||||
|
||||
let visible = false
|
||||
let upload_queue: UploadJob[] = [];
|
||||
let visible = $state(false)
|
||||
let upload_queue: UploadJob[] = $state([]);
|
||||
let task_id_counter = 0
|
||||
|
||||
export const upload_files = async (files: File[]|FileList) => {
|
||||
@@ -76,8 +78,8 @@ export const upload_file = async (file: File) => {
|
||||
// each upload progress bar will have bound itself to its array item
|
||||
upload_queue = upload_queue
|
||||
|
||||
if (active_uploads === 0 && state !== "uploading") {
|
||||
state = "uploading"
|
||||
if (active_uploads === 0 && status !== "uploading") {
|
||||
status = "uploading"
|
||||
visible = true
|
||||
await tick()
|
||||
await start_upload()
|
||||
@@ -85,7 +87,7 @@ export const upload_file = async (file: File) => {
|
||||
}
|
||||
|
||||
let active_uploads = 0
|
||||
let state = "idle"
|
||||
let status = $state("idle")
|
||||
|
||||
const start_upload = async () => {
|
||||
active_uploads = 0
|
||||
@@ -115,7 +117,7 @@ const start_upload = async () => {
|
||||
}
|
||||
|
||||
if (active_uploads === 0) {
|
||||
state = "finished"
|
||||
status = "finished"
|
||||
nav.reload()
|
||||
|
||||
// Empty the queue to free any references to lingering components
|
||||
@@ -123,12 +125,12 @@ const start_upload = async () => {
|
||||
|
||||
// In ten seconds we close the popup
|
||||
setTimeout(() => {
|
||||
if (state === "finished") {
|
||||
if (status === "finished") {
|
||||
visible = false
|
||||
}
|
||||
}, 10000)
|
||||
} else {
|
||||
state = "uploading"
|
||||
status = "uploading"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,7 +141,7 @@ const finish_upload = () => {
|
||||
}
|
||||
|
||||
const leave_confirmation = (e: BeforeUnloadEvent) => {
|
||||
if (state === "uploading") {
|
||||
if (status === "uploading") {
|
||||
e.preventDefault()
|
||||
return "If you close this page your files will stop uploading. Do you want to continue?"
|
||||
} else {
|
||||
@@ -148,22 +150,22 @@ const leave_confirmation = (e: BeforeUnloadEvent) => {
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window on:beforeunload={leave_confirmation} />
|
||||
<svelte:window onbeforeunload={leave_confirmation} />
|
||||
|
||||
<input
|
||||
bind:this={file_input_field}
|
||||
on:change={file_input_change}
|
||||
onchange={file_input_change}
|
||||
class="upload_input" type="file" name="file" multiple
|
||||
/>
|
||||
|
||||
{#if visible}
|
||||
<div class="upload_widget">
|
||||
<div class="header">
|
||||
{#if state === "idle"}
|
||||
{#if status === "idle"}
|
||||
Waiting for files
|
||||
{:else if state === "uploading"}
|
||||
{:else if status === "uploading"}
|
||||
Uploading files...
|
||||
{:else if state === "finished"}
|
||||
{:else if status === "finished"}
|
||||
Done
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -6,11 +6,19 @@ import Button from "layout/Button.svelte"
|
||||
import type { UploadJob } from "./FSUploadWidget.svelte";
|
||||
|
||||
let dispatch = createEventDispatcher()
|
||||
export let job: UploadJob
|
||||
export let total = 0
|
||||
export let loaded = 0
|
||||
let error_code = ""
|
||||
let error_message = ""
|
||||
|
||||
let {
|
||||
job = $bindable(),
|
||||
total = $bindable(0),
|
||||
loaded = $bindable(0)
|
||||
}: {
|
||||
job: UploadJob;
|
||||
total?: number;
|
||||
loaded?: number;
|
||||
} = $props();
|
||||
|
||||
let error_code = $state("")
|
||||
let error_message = $state("")
|
||||
let xhr: XMLHttpRequest = null
|
||||
|
||||
export const start = () => {
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
<script lang="ts">
|
||||
import { preventDefault } from 'svelte/legacy';
|
||||
import type { FSNavigator } from "filesystem/FSNavigator";
|
||||
|
||||
export let nav: FSNavigator
|
||||
export let path = ""
|
||||
let {
|
||||
nav,
|
||||
path = "",
|
||||
children
|
||||
}: {
|
||||
nav: FSNavigator;
|
||||
path?: string;
|
||||
children?: import('svelte').Snippet;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<a href={"/d"+path} on:click|preventDefault={() => {nav.navigate(path, true)}}>
|
||||
<slot></slot>
|
||||
<a href={"/d"+path} onclick={preventDefault(() => {nav.navigate(path, true)})}>
|
||||
{@render children?.()}
|
||||
</a>
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
<script lang="ts">
|
||||
import { preventDefault } from 'svelte/legacy';
|
||||
import { onMount } from 'svelte'
|
||||
import { fs_path_url, fs_encode_path, fs_node_icon } from "lib/FilesystemAPI"
|
||||
import TextBlock from "layout/TextBlock.svelte"
|
||||
import type { FSNavigator } from 'filesystem/FSNavigator';
|
||||
|
||||
export let nav: FSNavigator
|
||||
let player: HTMLAudioElement
|
||||
let playing = false
|
||||
let { nav, children }: {
|
||||
nav: FSNavigator;
|
||||
children?: import('svelte').Snippet;
|
||||
} = $props();
|
||||
|
||||
let player: HTMLAudioElement = $state()
|
||||
let playing = $state(false)
|
||||
let media_session = false
|
||||
let siblings = []
|
||||
let siblings = $state([])
|
||||
|
||||
export const toggle_playback = () => playing ? player.pause() : player.play()
|
||||
export const toggle_mute = () => player.muted = !player.muted
|
||||
@@ -47,7 +52,7 @@ onMount(() => {
|
||||
})
|
||||
</script>
|
||||
|
||||
<slot></slot>
|
||||
{@render children?.()}
|
||||
|
||||
<TextBlock width="1000px">
|
||||
<audio
|
||||
@@ -56,30 +61,30 @@ onMount(() => {
|
||||
src={fs_path_url($nav.base.path)}
|
||||
autoplay
|
||||
controls
|
||||
on:pause={() => playing = false }
|
||||
on:play={() => playing = true }
|
||||
on:ended={() => nav.open_sibling(1) }>
|
||||
onpause={() => playing = false}
|
||||
onplay={() => playing = true}
|
||||
onended={() => nav.open_sibling(1)}>
|
||||
<track kind="captions"/>
|
||||
</audio>
|
||||
<div style="text-align: center;">
|
||||
<button on:click={() => nav.open_sibling(-1) }><i class="icon">skip_previous</i></button>
|
||||
<button on:click={() => seek(-10) }><i class="icon">replay_10</i></button>
|
||||
<button on:click={toggle_playback}>
|
||||
<button onclick={() => nav.open_sibling(-1)}><i class="icon">skip_previous</i></button>
|
||||
<button onclick={() => seek(-10)}><i class="icon">replay_10</i></button>
|
||||
<button onclick={toggle_playback}>
|
||||
{#if playing}
|
||||
<i class="icon">pause</i>
|
||||
{:else}
|
||||
<i class="icon">play_arrow</i>
|
||||
{/if}
|
||||
</button>
|
||||
<button on:click={() => seek(10) }><i class="icon">forward_10</i></button>
|
||||
<button on:click={() => nav.open_sibling(1) }><i class="icon">skip_next</i></button>
|
||||
<button onclick={() => seek(10)}><i class="icon">forward_10</i></button>
|
||||
<button onclick={() => nav.open_sibling(1)}><i class="icon">skip_next</i></button>
|
||||
</div>
|
||||
|
||||
<h2>Tracklist</h2>
|
||||
{#each siblings as sibling (sibling.path)}
|
||||
<a
|
||||
href={"/d"+fs_encode_path(sibling.path)}
|
||||
on:click|preventDefault={() => nav.navigate(sibling.path, true)}
|
||||
onclick={preventDefault(() => nav.navigate(sibling.path, true))}
|
||||
class="node"
|
||||
>
|
||||
{#if sibling.path === $nav.base.path}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
<script lang="ts">
|
||||
export let path = []
|
||||
import type { FSNode } from 'lib/FilesystemAPI';
|
||||
import { run } from 'svelte/legacy';
|
||||
|
||||
let image_uri: string
|
||||
let image_link: string
|
||||
$: update_links(path)
|
||||
const update_links = (path) => {
|
||||
let { path = [] }: {path: FSNode[]} = $props();
|
||||
|
||||
let image_uri: string = $state()
|
||||
let image_link: string = $state()
|
||||
const update_links = (path: FSNode[]) => {
|
||||
image_uri = null
|
||||
image_link = null
|
||||
for (let node of path) {
|
||||
@@ -18,6 +20,9 @@ const update_links = (path) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
run(() => {
|
||||
update_links(path)
|
||||
});
|
||||
</script>
|
||||
|
||||
{#if image_uri}
|
||||
|
||||
@@ -8,10 +8,13 @@ import type { FSNavigator } from "filesystem/FSNavigator";
|
||||
|
||||
let dispatch = createEventDispatcher()
|
||||
|
||||
export let nav: FSNavigator
|
||||
let { nav, children }: {
|
||||
nav: FSNavigator;
|
||||
children?: import('svelte').Snippet;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<slot></slot>
|
||||
{@render children?.()}
|
||||
|
||||
<h1>{$nav.base.name}</h1>
|
||||
|
||||
@@ -20,11 +23,11 @@ export let nav: FSNavigator
|
||||
Size: {formatDataVolume($nav.base.file_size, 3)}<br/>
|
||||
Upload date: {formatDate($nav.base.created, true, true, false)}
|
||||
<hr/>
|
||||
<button class="button_highlight" on:click={() => {dispatch("download")}}>
|
||||
<button class="button_highlight" onclick={() => {dispatch("download")}}>
|
||||
<i class="icon">download</i>
|
||||
<span>Download</span>
|
||||
</button>
|
||||
<button on:click={() => {dispatch("details")}}>
|
||||
<button onclick={() => {dispatch("details")}}>
|
||||
<i class="icon">help</i>
|
||||
<span>Details</span>
|
||||
</button>
|
||||
|
||||
@@ -16,12 +16,14 @@ import type { FSNavigator } from "filesystem/FSNavigator";
|
||||
import FsUploadWidget from "filesystem/upload_widget/FSUploadWidget.svelte";
|
||||
import EditWindow from "filesystem/edit_window/EditWindow.svelte";
|
||||
|
||||
export let nav: FSNavigator
|
||||
export let upload_widget: FsUploadWidget
|
||||
export let edit_window: EditWindow
|
||||
let { nav, upload_widget, edit_window }: {
|
||||
nav: FSNavigator;
|
||||
upload_widget: FsUploadWidget;
|
||||
edit_window: EditWindow;
|
||||
} = $props();
|
||||
|
||||
let viewer: any
|
||||
let viewer_type = ""
|
||||
let viewer: any = $state()
|
||||
let viewer_type = $state("")
|
||||
let last_path = ""
|
||||
|
||||
onMount(() => nav.subscribe(state_update))
|
||||
|
||||
@@ -6,13 +6,16 @@ import type { FSNavigator } from "filesystem/FSNavigator";
|
||||
|
||||
let dispatch = createEventDispatcher();
|
||||
|
||||
export let nav: FSNavigator
|
||||
let container: HTMLDivElement
|
||||
let zoom = false
|
||||
let { nav }: {
|
||||
nav: FSNavigator;
|
||||
} = $props();
|
||||
|
||||
let container: HTMLDivElement = $state()
|
||||
let zoom = $state(false)
|
||||
let x = 0, y = 0
|
||||
let dragging = false
|
||||
let swipe_prev = true
|
||||
let swipe_next = true
|
||||
let swipe_prev = $state(true)
|
||||
let swipe_next = $state(true)
|
||||
|
||||
export const update = async () => {
|
||||
dispatch("loading", true)
|
||||
@@ -66,7 +69,7 @@ const mouseup = (e: MouseEvent) => {
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window on:mousemove={mousemove} on:mouseup={mouseup} />
|
||||
<svelte:window onmousemove={mousemove} onmouseup={mouseup} />
|
||||
|
||||
<div
|
||||
bind:this={container}
|
||||
@@ -80,12 +83,12 @@ const mouseup = (e: MouseEvent) => {
|
||||
on_next: () => nav.open_sibling(1),
|
||||
}}
|
||||
>
|
||||
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
|
||||
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
|
||||
<img
|
||||
on:dblclick={() => {zoom = !zoom}}
|
||||
on:mousedown={mousedown}
|
||||
on:load={on_load}
|
||||
on:error={on_load}
|
||||
ondblclick={() => {zoom = !zoom}}
|
||||
onmousedown={mousedown}
|
||||
onload={on_load}
|
||||
onerror={on_load}
|
||||
class="image"
|
||||
class:zoom
|
||||
src={fs_path_url($nav.base.path)}
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
import { fs_path_url } from "lib/FilesystemAPI";
|
||||
import type { FSNavigator } from "filesystem/FSNavigator";
|
||||
|
||||
export let nav: FSNavigator
|
||||
let { nav }: {
|
||||
nav: FSNavigator;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<iframe
|
||||
|
||||
@@ -3,8 +3,12 @@ import { tick } from "svelte";
|
||||
import { fs_path_url, type FSNode } from "lib/FilesystemAPI";
|
||||
import type { FSNavigator } from "filesystem/FSNavigator";
|
||||
|
||||
export let nav: FSNavigator
|
||||
let text_type = "text"
|
||||
let { nav, children }: {
|
||||
nav: FSNavigator;
|
||||
children?: import('svelte').Snippet;
|
||||
} = $props();
|
||||
|
||||
let text_type = $state("text")
|
||||
|
||||
export const update = () => {
|
||||
console.debug("Loading text file", nav.base.name)
|
||||
@@ -25,7 +29,7 @@ export const update = () => {
|
||||
}
|
||||
}
|
||||
|
||||
let text_pre: HTMLPreElement
|
||||
let text_pre: HTMLPreElement = $state()
|
||||
const text = async (file: FSNode) => {
|
||||
text_type = "text"
|
||||
await tick()
|
||||
@@ -42,7 +46,7 @@ const text = async (file: FSNode) => {
|
||||
})
|
||||
}
|
||||
|
||||
let md_container: HTMLElement
|
||||
let md_container: HTMLElement = $state()
|
||||
const markdown = async (file: FSNode) => {
|
||||
text_type = "markdown"
|
||||
await tick()
|
||||
@@ -61,7 +65,7 @@ const markdown = async (file: FSNode) => {
|
||||
</script>
|
||||
|
||||
<div class="container">
|
||||
<slot></slot>
|
||||
{@render children?.()}
|
||||
|
||||
{#if text_type === "markdown"}
|
||||
<section bind:this={md_container} class="md">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<script lang="ts" context="module">
|
||||
<script lang="ts" module>
|
||||
export type TorrentInfo = {
|
||||
trackers: string[]
|
||||
comment: string,
|
||||
@@ -26,9 +26,12 @@ import { loading_finish, loading_start } from "lib/Loading";
|
||||
|
||||
let dispatch = createEventDispatcher()
|
||||
|
||||
export let nav: FSNavigator
|
||||
let { nav, children }: {
|
||||
nav: FSNavigator;
|
||||
children?: import('svelte').Snippet;
|
||||
} = $props();
|
||||
|
||||
let status = "loading"
|
||||
let status = $state("loading")
|
||||
|
||||
export const update = async () => {
|
||||
try {
|
||||
@@ -64,11 +67,11 @@ export const update = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
let torrent: TorrentInfo = {} as TorrentInfo
|
||||
let magnet = ""
|
||||
let torrent: TorrentInfo = $state({} as TorrentInfo)
|
||||
let magnet = $state("")
|
||||
</script>
|
||||
|
||||
<slot></slot>
|
||||
{@render children?.()}
|
||||
|
||||
<h1>{$nav.base.name}</h1>
|
||||
|
||||
@@ -93,7 +96,7 @@ let magnet = ""
|
||||
Torrent file could not be parsed. It may be corrupted.
|
||||
</p>
|
||||
{/if}
|
||||
<button on:click={() => {dispatch("download")}} class="button">
|
||||
<button onclick={() => {dispatch("download")}} class="button">
|
||||
<i class="icon">download</i>
|
||||
<span>Download torrent file</span>
|
||||
</button>
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
<script lang="ts">
|
||||
import TorrentItem from './TorrentItem.svelte';
|
||||
import { formatDataVolume } from "util/Formatting";
|
||||
import type { TorrentFile } from "./Torrent.svelte";
|
||||
|
||||
export let item: TorrentFile = {} as TorrentFile
|
||||
let { item = {} as TorrentFile }: {
|
||||
item?: TorrentFile;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<ul class="list_open">
|
||||
@@ -10,7 +13,7 @@ export let item: TorrentFile = {} as TorrentFile
|
||||
<li class:list_closed={!child.children}>
|
||||
{name} ({formatDataVolume(child.size, 3)})<br/>
|
||||
{#if child.children}
|
||||
<svelte:self item={child}></svelte:self>
|
||||
<TorrentItem item={child}></TorrentItem>
|
||||
{/if}
|
||||
</li>
|
||||
{/each}
|
||||
|
||||
@@ -5,16 +5,18 @@ import { fs_path_url } from "lib/FilesystemAPI";
|
||||
import type { FSNavigator } from "filesystem/FSNavigator";
|
||||
let dispatch = createEventDispatcher()
|
||||
|
||||
export let nav: FSNavigator
|
||||
let { nav }: {
|
||||
nav: FSNavigator;
|
||||
} = $props();
|
||||
|
||||
// Used to detect when the file path changes
|
||||
let last_path = ""
|
||||
let loaded = false
|
||||
let loaded = $state(false)
|
||||
|
||||
let player: HTMLVideoElement
|
||||
let playing = false
|
||||
let player: HTMLVideoElement = $state()
|
||||
let playing = $state(false)
|
||||
let media_session = false
|
||||
let loop = false
|
||||
let loop = $state(false)
|
||||
|
||||
export const update = async () => {
|
||||
if (media_session) {
|
||||
@@ -85,8 +87,7 @@ const video_keydown = (e: KeyboardEvent) => {
|
||||
{#if
|
||||
$nav.base.file_type === "video/x-matroska" ||
|
||||
$nav.base.file_type === "video/quicktime" ||
|
||||
$nav.base.file_type === "video/x-ms-asf"
|
||||
}
|
||||
$nav.base.file_type === "video/x-ms-asf"}
|
||||
<div class="compatibility_warning">
|
||||
This video file type is not compatible with every web
|
||||
browser. If the video fails to play you can try downloading
|
||||
@@ -96,7 +97,7 @@ const video_keydown = (e: KeyboardEvent) => {
|
||||
<div class="player_and_controls">
|
||||
<div class="player">
|
||||
{#if loaded}
|
||||
<!-- svelte-ignore a11y-media-has-caption -->
|
||||
<!-- svelte-ignore a11y_media_has_caption -->
|
||||
<video
|
||||
bind:this={player}
|
||||
controls
|
||||
@@ -104,9 +105,9 @@ const video_keydown = (e: KeyboardEvent) => {
|
||||
autoplay
|
||||
loop={loop}
|
||||
class="video"
|
||||
on:pause={() => playing = false }
|
||||
on:play={() => playing = true }
|
||||
on:keydown={video_keydown}
|
||||
onpause={() => playing = false}
|
||||
onplay={() => playing = true}
|
||||
onkeydown={video_keydown}
|
||||
use:video_position={() => $nav.base.sha256_sum.substring(0, 8)}
|
||||
>
|
||||
<source src={fs_path_url($nav.base.path)} type={$nav.base.file_type} />
|
||||
@@ -116,34 +117,34 @@ const video_keydown = (e: KeyboardEvent) => {
|
||||
|
||||
<div class="controls">
|
||||
<div class="spacer"></div>
|
||||
<button on:click={() => dispatch("open_sibling", -1) }>
|
||||
<button onclick={() => dispatch("open_sibling", -1)}>
|
||||
<i class="icon">skip_previous</i>
|
||||
</button>
|
||||
<button on:click={() => seek(-10)}>
|
||||
<button onclick={() => seek(-10)}>
|
||||
<i class="icon">replay_10</i>
|
||||
</button>
|
||||
<button on:click={toggle_playback} class="button_highlight">
|
||||
<button onclick={toggle_playback} class="button_highlight">
|
||||
{#if playing}
|
||||
<i class="icon">pause</i>
|
||||
{:else}
|
||||
<i class="icon">play_arrow</i>
|
||||
{/if}
|
||||
</button>
|
||||
<button on:click={() => seek(10)}>
|
||||
<button onclick={() => seek(10)}>
|
||||
<i class="icon">forward_10</i>
|
||||
</button>
|
||||
<button on:click={() => dispatch("open_sibling", 1) }>
|
||||
<button onclick={() => dispatch("open_sibling", 1)}>
|
||||
<i class="icon">skip_next</i>
|
||||
</button>
|
||||
<div style="width: 16px; height: 8px;"></div>
|
||||
<button on:click={toggle_mute} class:button_red={player && player.muted}>
|
||||
<button onclick={toggle_mute} class:button_red={player && player.muted}>
|
||||
{#if player && player.muted}
|
||||
<i class="icon">volume_off</i>
|
||||
{:else}
|
||||
<i class="icon">volume_up</i>
|
||||
{/if}
|
||||
</button>
|
||||
<button on:click={toggle_fullscreen}>
|
||||
<button onclick={toggle_fullscreen}>
|
||||
<i class="icon">fullscreen</i>
|
||||
</button>
|
||||
<div class="spacer"></div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<script lang="ts" context="module">
|
||||
<script lang="ts" module>
|
||||
export type ZipEntry = {
|
||||
size: number,
|
||||
children?: {[index: string]: ZipEntry},
|
||||
@@ -19,15 +19,18 @@ import { loading_finish, loading_start } from "lib/Loading";
|
||||
|
||||
let dispatch = createEventDispatcher()
|
||||
|
||||
export let nav: FSNavigator
|
||||
let { nav, children }: {
|
||||
nav: FSNavigator;
|
||||
children?: import('svelte').Snippet;
|
||||
} = $props();
|
||||
|
||||
let status = "loading"
|
||||
let status = $state("loading")
|
||||
|
||||
let zip: ZipEntry = {size: 0} as ZipEntry
|
||||
let zip: ZipEntry = $state({size: 0} as ZipEntry)
|
||||
let uncomp_size = 0
|
||||
let comp_ratio = 0
|
||||
let archive_type = ""
|
||||
let truncated = false
|
||||
let comp_ratio = $state(0)
|
||||
let archive_type = $state("")
|
||||
let truncated = $state(false)
|
||||
|
||||
export const update = async () => {
|
||||
if (nav.base.file_type === "application/x-7z-compressed") {
|
||||
@@ -93,7 +96,7 @@ const recursive_size = (file: ZipEntry) => {
|
||||
}
|
||||
</script>
|
||||
|
||||
<slot></slot>
|
||||
{@render children?.()}
|
||||
|
||||
<h1>{$nav.base.name}</h1>
|
||||
|
||||
@@ -110,7 +113,7 @@ const recursive_size = (file: ZipEntry) => {
|
||||
{/if}
|
||||
Uploaded on: {formatDate($nav.base.created, true, true, true)}
|
||||
<br/>
|
||||
<button class="button_highlight" on:click={() => {dispatch("download")}}>
|
||||
<button class="button_highlight" onclick={() => {dispatch("download")}}>
|
||||
<i class="icon">download</i>
|
||||
<span>Download</span>
|
||||
</button>
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
<script lang="ts">
|
||||
import ZipItem from './ZipItem.svelte';
|
||||
import type { ZipEntry } from "filesystem/viewers/Zip.svelte";
|
||||
import { formatDataVolume } from "util/Formatting";
|
||||
|
||||
export let item: ZipEntry = {} as ZipEntry
|
||||
let { item = {} as ZipEntry }: {
|
||||
item?: ZipEntry;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<!-- First get directories and render them as details collapsibles -->
|
||||
@@ -23,7 +26,7 @@ export let item: ZipEntry = {} as ZipEntry
|
||||
|
||||
<!-- Performance optimization, only render children if details is expanded -->
|
||||
{#if child.details_open}
|
||||
<svelte:self item={child}></svelte:self>
|
||||
<ZipItem item={child}></ZipItem>
|
||||
{/if}
|
||||
</details>
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user