|
|
@@ -1,11 +1,52 @@
|
|
|
<script>
|
|
|
+ import {notify} from "$lib/notifier.svelte.js";
|
|
|
+ import {goto} from "$app/navigation";
|
|
|
+
|
|
|
let {data} = $props();
|
|
|
+ let uploadInput = $state();
|
|
|
+
|
|
|
+ const uploadRecipe = async ()=>{
|
|
|
+ const file = uploadInput.files[0];
|
|
|
+ uploadInput.value = "";
|
|
|
+ if(!file) return;
|
|
|
+
|
|
|
+ try{
|
|
|
+ const text = await file.text();
|
|
|
+ const response = await fetch("/recipe/new", {
|
|
|
+ method: "POST",
|
|
|
+ headers: {"Content-Type": "application/json"},
|
|
|
+ credentials: "include",
|
|
|
+ body: text
|
|
|
+ });
|
|
|
+
|
|
|
+ let responseData = await response.json();
|
|
|
+ if(!response.ok) return notify("error", responseData.msg);
|
|
|
+
|
|
|
+ notify("success", "Recipe added");
|
|
|
+ goto(`/recipe/${responseData.id}`);
|
|
|
+ }catch(e){
|
|
|
+ notify("error", "Failed to parse the recipe");
|
|
|
+ }
|
|
|
+ }
|
|
|
</script>
|
|
|
|
|
|
<div class="container">
|
|
|
<div class="topbar">
|
|
|
<h1>Recipes</h1>
|
|
|
- <a href="/recipe/new" class="button">New Recipe</a>
|
|
|
+ <div class="share">
|
|
|
+ <button type="button" class="shareToggle" aria-haspopup="true">New Recipe</button>
|
|
|
+ <div class="shareMenu" role="menu">
|
|
|
+ <a href="/recipe/new" role="menuitem">Create</a>
|
|
|
+ <button role="menuitem" onclick={()=>{uploadInput.click()}}>Upload</button>
|
|
|
+ <input
|
|
|
+ bind:this={uploadInput}
|
|
|
+ onchange={uploadRecipe}
|
|
|
+ type="file"
|
|
|
+ accept=".json,application/json"
|
|
|
+ hidden
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
|
|
|
<div class="recipes">
|
|
|
@@ -46,10 +87,87 @@
|
|
|
min-width: 0;
|
|
|
}
|
|
|
|
|
|
- .button{
|
|
|
+ .shareToggle{
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ box-sizing: border-box;
|
|
|
+ border: none;
|
|
|
+ outline: none;
|
|
|
+ cursor: pointer;
|
|
|
margin: 0;
|
|
|
+ background: var(--color-primary);
|
|
|
+ color: var(--color-text-on-primary);
|
|
|
+ border-radius: 6px;
|
|
|
+ padding: 0.6rem 1.2rem;
|
|
|
+ font-weight: 600;
|
|
|
+ font-size: 1rem;
|
|
|
+ line-height: 1.2;
|
|
|
+ white-space: nowrap;
|
|
|
+ text-decoration: none;
|
|
|
+ transition: background 0.15s ease, transform 0.05s ease;
|
|
|
+ font-family: inherit;
|
|
|
+ }
|
|
|
+
|
|
|
+ .shareToggle:hover{
|
|
|
+ background: var(--color-primary-hover);
|
|
|
+ }
|
|
|
+
|
|
|
+ .shareToggle:active{
|
|
|
+ transform: translateY(1px);
|
|
|
+ }
|
|
|
+
|
|
|
+ .share{
|
|
|
+ position: relative;
|
|
|
flex-shrink: 0;
|
|
|
- text-align: center;
|
|
|
+ }
|
|
|
+
|
|
|
+ .shareMenu{
|
|
|
+ position: absolute;
|
|
|
+ right: 0;
|
|
|
+ top: calc(100% + 0.4rem);
|
|
|
+ z-index: 10;
|
|
|
+ display: none;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 0.2rem;
|
|
|
+ min-width: 100%;
|
|
|
+ padding: 0.35rem;
|
|
|
+ background: var(--color-surface);
|
|
|
+ border: 1px solid var(--color-border);
|
|
|
+ border-radius: 8px;
|
|
|
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.35);
|
|
|
+ }
|
|
|
+
|
|
|
+ .share:focus-within .shareMenu{
|
|
|
+ display: flex;
|
|
|
+ }
|
|
|
+
|
|
|
+ .shareMenu button,
|
|
|
+ .shareMenu a{
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ box-sizing: border-box;
|
|
|
+ border: none;
|
|
|
+ outline: none;
|
|
|
+ cursor: pointer;
|
|
|
+ margin: 0;
|
|
|
+ width: 100%;
|
|
|
+ padding: 0.55rem 0.8rem;
|
|
|
+ background: transparent;
|
|
|
+ color: var(--color-text);
|
|
|
+ border-radius: 6px;
|
|
|
+ font-weight: 600;
|
|
|
+ font-size: 0.9rem;
|
|
|
+ line-height: 1.2;
|
|
|
+ text-align: left;
|
|
|
+ white-space: nowrap;
|
|
|
+ text-decoration: none;
|
|
|
+ font-family: inherit;
|
|
|
+ }
|
|
|
+
|
|
|
+ .shareMenu button:hover,
|
|
|
+ .shareMenu a:hover{
|
|
|
+ background: var(--color-surface-alt);
|
|
|
}
|
|
|
|
|
|
.recipes{
|
|
|
@@ -121,11 +239,10 @@
|
|
|
}
|
|
|
|
|
|
@media (pointer: coarse){
|
|
|
- .button{
|
|
|
+ .shareToggle,
|
|
|
+ .shareMenu button,
|
|
|
+ .shareMenu a{
|
|
|
min-height: 44px;
|
|
|
- display: inline-flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
}
|
|
|
}
|
|
|
</style>
|