mirror of
https://github.com/Jackzmc/storage.git
synced 2025-05-05 20:53:21 +00:00
More UI polishing
This commit is contained in:
parent
f54cbbc8bd
commit
a1d740aa05
9 changed files with 190 additions and 64 deletions
|
@ -3,12 +3,13 @@ use std::io::BufReader;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use anyhow::Error;
|
use anyhow::Error;
|
||||||
use rocket::response::stream::ReaderStream;
|
use rocket::response::stream::ReaderStream;
|
||||||
|
use rocket::serde::Serialize;
|
||||||
use tokio::io::BufStream;
|
use tokio::io::BufStream;
|
||||||
use crate::managers::repos::RepoContainer;
|
use crate::managers::repos::RepoContainer;
|
||||||
use crate::{models, DB};
|
use crate::{models, DB};
|
||||||
use crate::models::library::LibraryModel;
|
use crate::models::library::LibraryModel;
|
||||||
use crate::models::repo::RepoModel;
|
use crate::models::repo::RepoModel;
|
||||||
use crate::storage::FileEntry;
|
use crate::storage::{FileEntry, FileType};
|
||||||
use crate::util::{JsonErrorResponse, ResponseError};
|
use crate::util::{JsonErrorResponse, ResponseError};
|
||||||
|
|
||||||
pub struct Library {
|
pub struct Library {
|
||||||
|
@ -33,6 +34,11 @@ impl Library {
|
||||||
repo.backend.get_read_stream(&self.model.id.to_string(), rel_path)
|
repo.backend.get_read_stream(&self.model.id.to_string(), rel_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn touch_file(&self, rel_path: &PathBuf, file_type: FileType) -> Result<(), anyhow::Error> {
|
||||||
|
let mut repo = self.repo.read().await;
|
||||||
|
repo.backend.touch_file(&self.model.id.to_string(), rel_path, file_type)
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn write_file(&self, rel_path: &PathBuf, contents: &[u8]) -> Result<(), anyhow::Error> {
|
pub async fn write_file(&self, rel_path: &PathBuf, contents: &[u8]) -> Result<(), anyhow::Error> {
|
||||||
let mut repo = self.repo.read().await;
|
let mut repo = self.repo.read().await;
|
||||||
repo.backend.write_file(&self.model.id.to_string(), rel_path, contents)
|
repo.backend.write_file(&self.model.id.to_string(), rel_path, contents)
|
||||||
|
|
|
@ -6,6 +6,7 @@ use rocket::fs::TempFile;
|
||||||
use rocket::http::Status;
|
use rocket::http::Status;
|
||||||
use rocket::response::status;
|
use rocket::response::status;
|
||||||
use rocket::serde::json::Json;
|
use rocket::serde::json::Json;
|
||||||
|
use rocket::serde::Serialize;
|
||||||
use sqlx::{query, Postgres};
|
use sqlx::{query, Postgres};
|
||||||
use sqlx::types::{Uuid};
|
use sqlx::types::{Uuid};
|
||||||
use tokio::io::AsyncReadExt;
|
use tokio::io::AsyncReadExt;
|
||||||
|
@ -16,7 +17,7 @@ use crate::managers::libraries::LibraryManager;
|
||||||
use crate::managers::repos::RepoManager;
|
use crate::managers::repos::RepoManager;
|
||||||
use crate::models::library::{LibraryModel, LibraryWithRepoModel};
|
use crate::models::library::{LibraryModel, LibraryWithRepoModel};
|
||||||
use crate::models::user;
|
use crate::models::user;
|
||||||
use crate::storage::FileEntry;
|
use crate::storage::{FileEntry, FileType};
|
||||||
use crate::util::{JsonErrorResponse, ResponseError};
|
use crate::util::{JsonErrorResponse, ResponseError};
|
||||||
#[get("/<library_id>")]
|
#[get("/<library_id>")]
|
||||||
pub(crate) async fn get_file(pool: &State<DB>, library_id: &str) -> Result<Option<Json<LibraryWithRepoModel>>, ResponseError> {
|
pub(crate) async fn get_file(pool: &State<DB>, library_id: &str) -> Result<Option<Json<LibraryWithRepoModel>>, ResponseError> {
|
||||||
|
@ -37,6 +38,18 @@ pub(crate) async fn list_files(libraries: &State<Arc<Mutex<LibraryManager>>>, li
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[post("/<library_id>/touch?<path>&<file_type>")]
|
||||||
|
pub(crate) async fn touch_files(libraries: &State<Arc<Mutex<LibraryManager>>>, library_id: &str, path: &str, file_type: FileType) -> Result<(), ResponseError> {
|
||||||
|
let libs = libraries.lock().await;
|
||||||
|
let library = libs.get(library_id).await?;
|
||||||
|
library.touch_file(&PathBuf::from(path), file_type).await
|
||||||
|
.map_err(|e| ResponseError::InternalServerError(JsonErrorResponse {
|
||||||
|
code: "STORAGE_ERROR".to_string(),
|
||||||
|
message: e.to_string(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
#[get("/<library_id>/files/download?<path>")]
|
#[get("/<library_id>/files/download?<path>")]
|
||||||
pub(crate) async fn download_file(libraries: &State<Arc<Mutex<LibraryManager>>>, library_id: &str, path: &str) -> Result<Vec<u8>, ResponseError> {
|
pub(crate) async fn download_file(libraries: &State<Arc<Mutex<LibraryManager>>>, library_id: &str, path: &str) -> Result<Vec<u8>, ResponseError> {
|
||||||
let libs = libraries.lock().await;
|
let libs = libraries.lock().await;
|
||||||
|
|
|
@ -61,7 +61,7 @@ pub fn get_backend(storage_type: &str, settings: &JsonValue) -> Result<Option<Bo
|
||||||
|
|
||||||
pub trait StorageBackend {
|
pub trait StorageBackend {
|
||||||
// fn new(settings: &JsonValue) -> Result<Self, StorageBackendError>;
|
// fn new(settings: &JsonValue) -> Result<Self, StorageBackendError>;
|
||||||
|
fn touch_file(&self, library_id: &str, rel_path: &PathBuf, file_type: FileType) -> Result<(), anyhow::Error>;
|
||||||
fn write_file(&self, library_id: &str, rel_path: &PathBuf, contents: &[u8]) -> Result<(), anyhow::Error>;
|
fn write_file(&self, library_id: &str, rel_path: &PathBuf, contents: &[u8]) -> Result<(), anyhow::Error>;
|
||||||
|
|
||||||
fn read_file(&self, library_id: &str, rel_path: &PathBuf) -> Result<Option<Vec<u8>>, anyhow::Error>;
|
fn read_file(&self, library_id: &str, rel_path: &PathBuf) -> Result<Option<Vec<u8>>, anyhow::Error>;
|
||||||
|
|
|
@ -6,7 +6,7 @@ use std::path::{Path, PathBuf};
|
||||||
use anyhow::{anyhow, Error};
|
use anyhow::{anyhow, Error};
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use sqlx::types::JsonValue;
|
use sqlx::types::JsonValue;
|
||||||
use crate::storage::{FileEntry, StorageBackend};
|
use crate::storage::{FileEntry, FileType, StorageBackend};
|
||||||
|
|
||||||
pub struct LocalStorage {
|
pub struct LocalStorage {
|
||||||
folder_root: PathBuf
|
folder_root: PathBuf
|
||||||
|
@ -36,17 +36,25 @@ fn get_path(folder_root: &PathBuf, library_id: &str, mut path: &Path) -> Result<
|
||||||
Ok(path)
|
Ok(path)
|
||||||
}
|
}
|
||||||
impl StorageBackend for LocalStorage {
|
impl StorageBackend for LocalStorage {
|
||||||
|
fn touch_file(&self, library_id: &str, rel_path: &PathBuf, file_type: FileType) -> Result<(), anyhow::Error> {
|
||||||
fn get_read_stream(&self, library_id: &str, rel_path: &PathBuf,) -> Result<BufReader<File>, Error> {
|
|
||||||
let path = get_path(&self.folder_root, library_id, rel_path)?;
|
let path = get_path(&self.folder_root, library_id, rel_path)?;
|
||||||
let file = File::open(path)?;
|
match file_type {
|
||||||
Ok(BufReader::new(file))
|
FileType::File => {
|
||||||
|
// open and close file
|
||||||
|
File::open(path).map_err(|e| anyhow!(e))?;
|
||||||
|
}
|
||||||
|
FileType::Folder => {
|
||||||
|
std::fs::create_dir_all(path).map_err(|e| anyhow!(e))?;
|
||||||
|
}
|
||||||
|
_ => return Err(anyhow!("Unsupported"))
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_file(&self, library_id: &str, rel_path: &PathBuf, contents: &[u8]) -> Result<(), Error> {
|
fn write_file(&self, library_id: &str, rel_path: &PathBuf, contents: &[u8]) -> Result<(), Error> {
|
||||||
let path = get_path(&self.folder_root, library_id, rel_path)?;
|
let path = get_path(&self.folder_root, library_id, rel_path)?;
|
||||||
std::fs::write(path, contents).map_err(|e| anyhow!(e))
|
std::fs::write(path, contents).map_err(|e| anyhow!(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_file(&self, library_id: &str, rel_path: &PathBuf) -> Result<Option<Vec<u8>>, Error> {
|
fn read_file(&self, library_id: &str, rel_path: &PathBuf) -> Result<Option<Vec<u8>>, Error> {
|
||||||
let path = get_path(&self.folder_root, library_id, rel_path)?;
|
let path = get_path(&self.folder_root, library_id, rel_path)?;
|
||||||
match std::fs::read(path) {
|
match std::fs::read(path) {
|
||||||
|
@ -55,7 +63,6 @@ impl StorageBackend for LocalStorage {
|
||||||
Err(e) => Err(anyhow!(e)),
|
Err(e) => Err(anyhow!(e)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list_files(&self, library_id: &str, rel_path: &PathBuf) -> Result<Vec<FileEntry>, Error> {
|
fn list_files(&self, library_id: &str, rel_path: &PathBuf) -> Result<Vec<FileEntry>, Error> {
|
||||||
let path = get_path(&self.folder_root, library_id, rel_path)?;
|
let path = get_path(&self.folder_root, library_id, rel_path)?;
|
||||||
Ok(std::fs::read_dir(path)?
|
Ok(std::fs::read_dir(path)?
|
||||||
|
@ -73,14 +80,20 @@ impl StorageBackend for LocalStorage {
|
||||||
.collect())
|
.collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delete_file(&self, library_id: &str, rel_path: &PathBuf) -> Result<(), Error> {
|
fn delete_file(&self, library_id: &str, rel_path: &PathBuf) -> Result<(), Error> {
|
||||||
let path = get_path(&self.folder_root, library_id, rel_path)?;
|
let path = get_path(&self.folder_root, library_id, rel_path)?;
|
||||||
// TODO: check if folder?
|
// TODO: check if folder?
|
||||||
std::fs::remove_file(path).map_err(|e| anyhow!(e))
|
std::fs::remove_file(path).map_err(|e| anyhow!(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn move_file(&self, library_id: &str, rel_path: &PathBuf, new_rel_path: &PathBuf) -> Result<(), Error> {
|
fn move_file(&self, library_id: &str, rel_path: &PathBuf, new_rel_path: &PathBuf) -> Result<(), Error> {
|
||||||
let path = get_path(&self.folder_root, library_id, rel_path)?;
|
let path = get_path(&self.folder_root, library_id, rel_path)?;
|
||||||
std::fs::rename(path, new_rel_path).map_err(|e| anyhow!(e))
|
std::fs::rename(path, new_rel_path).map_err(|e| anyhow!(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_read_stream(&self, library_id: &str, rel_path: &PathBuf,) -> Result<BufReader<File>, Error> {
|
||||||
|
let path = get_path(&self.folder_root, library_id, rel_path)?;
|
||||||
|
let file = File::open(path)?;
|
||||||
|
Ok(BufReader::new(file))
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -78,4 +78,8 @@ tr.file-list td.filecell-label a {
|
||||||
}
|
}
|
||||||
.login-bg {
|
.login-bg {
|
||||||
background-image: linear-gradient(to top, #fbc2eb 0%, #a6c1ee 100%);
|
background-image: linear-gradient(to top, #fbc2eb 0%, #a6c1ee 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.readyhidden, *[x-cloak] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,16 @@
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
const addDropdown = document.getElementById("add-dropdown")
|
document.querySelector("#file-checkbox-all").addEventListener("input", (e) => {
|
||||||
addDropdown.classList.remove("is-hidden")
|
const checked = e.target.checked
|
||||||
|
console.log("checked", checked)
|
||||||
|
document.querySelectorAll(".file-checkbox").forEach(el => {
|
||||||
|
el.checked = checked
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const readyhiddenItems = document.getElementsByClassName("readyhidden");
|
||||||
|
for (let i = 0; i < readyhiddenItems.length; i++) {
|
||||||
|
readyhiddenItems.item(i).classList.remove("readyhidden")
|
||||||
|
}
|
||||||
|
|
||||||
document.getElementById("modal-prompt-form")
|
document.getElementById("modal-prompt-form")
|
||||||
|
|
||||||
|
@ -72,4 +82,4 @@ async function touchSubmit(event) {
|
||||||
}
|
}
|
||||||
function upload(type) {
|
function upload(type) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,4 +17,7 @@
|
||||||
<body class="{{body-class}}">
|
<body class="{{body-class}}">
|
||||||
{{> @partial-block }}
|
{{> @partial-block }}
|
||||||
</body>
|
</body>
|
||||||
|
{{#if has-scripts}}
|
||||||
|
{{> scripts}}
|
||||||
|
{{/if}}
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,28 +1,8 @@
|
||||||
{{#> layouts/main }}
|
{{#> layouts/main has-scripts=1 }}
|
||||||
<div class="">
|
<div id="app" x-data="{ touchPrompt: null }">
|
||||||
<div class="modal" id="modal-prompt">
|
|
||||||
<div class="modal-background"></div>
|
|
||||||
<div class="modal-card is-radiusless">
|
|
||||||
<form onsubmit="touchSubmit(event)">
|
|
||||||
<header class="modal-card-head is-radiusless py-5">
|
|
||||||
<p class="modal-card-title" id="modal-prompt-title">New Item</p>
|
|
||||||
<button class="delete" aria-label="close"></button>
|
|
||||||
</header>
|
|
||||||
<section class="modal-card-body is-radiusless py-4">
|
|
||||||
<input autocomplete="off" id="modal-prompt-type" required type="hidden">
|
|
||||||
<input autocomplete="off" id="modal-prompt-input" required type="text" class="input" placeholder="">
|
|
||||||
</section>
|
|
||||||
<footer class="modal-card-foot is-radiusless py-3">
|
|
||||||
<div class="buttons">
|
|
||||||
<input type="submit" class="button is-primary" value="Create"></input>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<nav class="breadcrumb is-inline-block is-size-5 mb-0" aria-label="breadcrumbs">
|
<nav class="breadcrumb is-inline-block is-size-5 mb-0" aria-label="breadcrumbs">
|
||||||
<ul>
|
<ul>
|
||||||
<li><a class="has-text-black" href="/library/{{library.id}}/{{library.name}}/">{{ library.name }}</a></li>
|
<li><a class="has-text-black has-text-link" href="/library/{{library.id}}/{{library.name}}/">{{ library.name }}</a></li>
|
||||||
{{#each path_segments}}
|
{{#each path_segments}}
|
||||||
<li>
|
<li>
|
||||||
<a class="has-text-black" href="/library/{{../library.id}}/{{../library.name}}/{{path}}" aria-current="page"> {{ segment }} </a>
|
<a class="has-text-black" href="/library/{{../library.id}}/{{../library.name}}/{{path}}" aria-current="page"> {{ segment }} </a>
|
||||||
|
@ -30,9 +10,10 @@
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="dropdown is-hoverable is-hidden" id="add-dropdown">
|
<span x-text="touchPrompt" ></span>
|
||||||
|
<div class="dropdown is-hoverable" id="add-dropdown" x-cloak>
|
||||||
<div class="dropdown-trigger">
|
<div class="dropdown-trigger">
|
||||||
<button class="button is-small" aria-haspopup="true" aria-controls="dropdown-menu">
|
<button class="button is-small has-background-white-ter" aria-haspopup="true" aria-controls="dropdown-menu">
|
||||||
<span class="icon is-small">
|
<span class="icon is-small">
|
||||||
<i class="fas fa-plus"></i>
|
<i class="fas fa-plus"></i>
|
||||||
</span>
|
</span>
|
||||||
|
@ -40,28 +21,68 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="dropdown-menu" role="menu">
|
<div class="dropdown-menu" role="menu">
|
||||||
<div class="dropdown-content">
|
<div class="dropdown-content">
|
||||||
<a onclick="touch('file')" id="new_file" href="#" class="dropdown-item"> New File </a>
|
<a x-on:click="touchPrompt = 'file'" id="new_file" href="#" class="dropdown-item"> New File </a>
|
||||||
<a onclick="touch('folder')"id="new_folder" class="dropdown-item"> New Folder </a>
|
<a onclick="touch('folder')" id="new_folder" class="dropdown-item"> New Folder </a>
|
||||||
<hr class="dropdown-divider" />
|
<hr class="dropdown-divider" />
|
||||||
<a onclick="upload('file')" id="upload_file" href="#" class="dropdown-item"> Upload File </a>
|
<a onclick="upload('file')" id="upload_file" href="#" class="dropdown-item"> Upload File </a>
|
||||||
<a onclick="upload('folder')" id="upload_folder" href="#" class="dropdown-item"> Upload Folder </a>
|
<a onclick="upload('folder')" id="upload_folder" href="#" class="dropdown-item"> Upload Folder </a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<noscript><em>Javascript required to create/upload files</noscript>
|
<noscript><em>Javascript required to create/upload files</em></noscript>
|
||||||
<div class="is-pulled-right is-inline-block">
|
<div class="is-pulled-right is-inline-block">
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<div class="button is-small">
|
<div class="dropdown is-hoverable" id="dropdown-display" x-cloak>
|
||||||
Display
|
<div class="dropdown-trigger">
|
||||||
|
<button class="button is-small has-background-white-ter" aria-haspopup="true" aria-controls="dropdown-menu">
|
||||||
|
<span class="icon">
|
||||||
|
<i class="fa fa-list is-small"></i>
|
||||||
|
</span>
|
||||||
|
<span>List</span>
|
||||||
|
<span class="icon is-small">
|
||||||
|
<i class="fas fa-angle-down" aria-hidden="true"></i>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="dropdown-menu" role="menu">
|
||||||
|
<div class="dropdown-content">
|
||||||
|
<a href="#" class="dropdown-item is-active"> <i class="fa fa-list"></i> List View</a>
|
||||||
|
<a class="dropdown-item"> <i class="fa fa-grip"></i> Grid View</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="button is-small">
|
<div class="dropdown is-hoverable" id="dropdown-sort" x-cloak>
|
||||||
Sort
|
<div class="dropdown-trigger">
|
||||||
|
<button class="button is-small has-background-white-ter" aria-haspopup="true" aria-controls="dropdown-menu">
|
||||||
|
<span class="icon">
|
||||||
|
<i class="fa fa-arrow-up is-small"></i>
|
||||||
|
</span>
|
||||||
|
<span>Sort by Name (asc)</span>
|
||||||
|
<span class="icon is-small">
|
||||||
|
<i class="fas fa-angle-down" aria-hidden="true"></i>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="dropdown-menu" role="menu">
|
||||||
|
<div class="dropdown-content">
|
||||||
|
<a href="#" class="dropdown-item is-active"> By name ascending</a>
|
||||||
|
<a class="dropdown-item"> By name descending</a>
|
||||||
|
<a class="dropdown-item"> By last modified ascending</a>
|
||||||
|
<a class="dropdown-item"> By last modified descending</a>
|
||||||
|
<a class="dropdown-item"> By size ascending</a>
|
||||||
|
<a class="dropdown-item"> By size ascending</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="button is-small">
|
<div class="button is-small has-background-white-ter">
|
||||||
Info
|
<span class="icon">
|
||||||
|
<i class="fa fa-info"></i>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="button is-small">
|
<div class="button is-small has-background-white-ter">
|
||||||
...
|
<span class="icon">
|
||||||
|
<i class="fa fa-ellipsis"></i>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -69,7 +90,7 @@
|
||||||
<table class="table is-fullwidth">
|
<table class="table is-fullwidth">
|
||||||
<thead>
|
<thead>
|
||||||
<tr class="file-list">
|
<tr class="file-list">
|
||||||
<td style="width:0"><input type="checkbox" /></td>
|
<td style="width:0"><input type="checkbox" id="file-checkbox-all" /></td>
|
||||||
<td style="width:0"></td>
|
<td style="width:0"></td>
|
||||||
<td style="width:0"></td>
|
<td style="width:0"></td>
|
||||||
<td>Name </td>
|
<td>Name </td>
|
||||||
|
@ -81,11 +102,15 @@
|
||||||
<tbody>
|
<tbody>
|
||||||
{{#each files }}
|
{{#each files }}
|
||||||
<tr class="file-list">
|
<tr class="file-list">
|
||||||
<td><input type="checkbox" /></td>
|
<td><input type="checkbox" class="file-checkbox" /></td>
|
||||||
<td>
|
<td>
|
||||||
<a class="has-text-black">
|
<a class="has-text-black">
|
||||||
<span class="icon is-large">
|
<span class="icon is-large">
|
||||||
<i class="fas fa-star fa-xl"></i>
|
{{#if favorited}}
|
||||||
|
<i class="fas fa-star fa-xl"></i>
|
||||||
|
{{else}}
|
||||||
|
<i class="far fa-star fa-xl"></i>
|
||||||
|
{{/if}}
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
|
@ -114,11 +139,63 @@
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
<div class="">
|
||||||
|
<div :class="{modal: true, 'is-active': touchPrompt != null}" id="modal-prompt" x-cloak>
|
||||||
|
<div class="modal-background"></div>
|
||||||
|
<div class="modal-card is-radiusless">
|
||||||
|
<form onsubmit="touchSubmit(event)">
|
||||||
|
<header class="modal-card-head is-radiusless py-4">
|
||||||
|
<p class="modal-card-title" id="modal-prompt-title">New Item</p>
|
||||||
|
<button class="delete" aria-label="close"></button>
|
||||||
|
</header>
|
||||||
|
<section class="modal-card-body is-radiusless py-4">
|
||||||
|
<input autocomplete="off" id="modal-prompt-type" required type="hidden">
|
||||||
|
<input focus autocomplete="off" id="modal-prompt-input" required type="text" class="input" placeholder="">
|
||||||
|
</section>
|
||||||
|
<footer class="modal-card-foot is-radiusless py-2">
|
||||||
|
<div class="buttons">
|
||||||
|
<input type="submit" class="button is-primary" value="Create" />
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/layouts/main}}
|
{{#*inline "scripts"}}
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const LIBRARY_ID = "{{ library.id }}";
|
const LIBRARY_ID = "{{ library.id }}";
|
||||||
const LIBRARY_PATH = "/{{ parent }}";
|
const LIBRARY_PATH = "/{{ parent }}";
|
||||||
</script>
|
</script>
|
||||||
<script src="/static/js/add_button.js"></script>
|
<script src="/static/js/add_button.js"></script>
|
||||||
|
<script>
|
||||||
|
document.addEventListener('alpine:init', () => {
|
||||||
|
console.info('Alpine init')
|
||||||
|
Alpine.data('game', () => game);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<script src="//unpkg.com/alpinejs" defer></script>
|
||||||
|
{{!-- <script type="module">
|
||||||
|
import { createApp, ref } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'
|
||||||
|
|
||||||
|
createApp({
|
||||||
|
delimiters: ['{%', '%}'],
|
||||||
|
setup() {
|
||||||
|
const message = ref('Hello Vue!')
|
||||||
|
const loaded = ref(false)
|
||||||
|
const touchPrompt = ref(false)
|
||||||
|
return {
|
||||||
|
loaded,
|
||||||
|
message,
|
||||||
|
touchPrompt
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
|
||||||
|
this.loaded = true
|
||||||
|
console.info("Vue ready")
|
||||||
|
}
|
||||||
|
}).mount('#app')
|
||||||
|
</script> --}}
|
||||||
|
{{/inline}}
|
||||||
|
{{/layouts/main}}
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
<div class="navbar-item" style="width:300px">
|
<div class="navbar-item" style="width:300px">
|
||||||
<div class="field" style="width:100%" >
|
<div class="field" style="width:100%" >
|
||||||
<p class="control has-icons-left">
|
<p class="control has-icons-left">
|
||||||
<input class="input is-small" type="search" placeholder="Search files (CTRL + K)"></input>
|
<input class="input is-small" type="search" placeholder="Search files (CTRL + K)" />
|
||||||
<span class="icon is-small is-left">
|
<span class="icon is-small is-left">
|
||||||
<i class="fa-solid fa-magnifying-glass"></i>
|
<i class="fa-solid fa-magnifying-glass"></i>
|
||||||
</span>
|
</span>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue