Update dependencies and component paths
This commit is contained in:
parent
bd1ad91efd
commit
23641c16e2
15 changed files with 92 additions and 115 deletions
29
src/lib/Banner.svelte
Normal file
29
src/lib/Banner.svelte
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<script>
|
||||
export let title = "Quartznet";
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.overlay {
|
||||
display: flex;
|
||||
background-image: url("/img/bluenebula.webp");
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
min-height: 200px;
|
||||
text-align: center;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.title {
|
||||
display: inline-block;
|
||||
font-size: 3rem;
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="overlay">
|
||||
<div class="description col-md-5">
|
||||
<h1 class="title">{title}</h1>
|
||||
<p>Software, Games, Science, Space!</p>
|
||||
</div>
|
||||
</div>
|
||||
274
src/lib/Modal.svelte
Normal file
274
src/lib/Modal.svelte
Normal file
|
|
@ -0,0 +1,274 @@
|
|||
<script lang="ts">
|
||||
//@ts-nocheck
|
||||
import {
|
||||
setContext as baseSetContext,
|
||||
createEventDispatcher,
|
||||
} from "svelte";
|
||||
import { fade } from "svelte/transition";
|
||||
|
||||
export let key = "simple-modal";
|
||||
export let closeButton = true;
|
||||
export let closeOnEsc = true;
|
||||
export let closeOnOuterClick = true;
|
||||
export let transitionBg = fade;
|
||||
export let transitionBgProps = { duration: 250 };
|
||||
export let transitionWindow = transitionBg;
|
||||
export let transitionWindowProps = transitionBgProps;
|
||||
export let styleBg = { top: 0, left: 0 };
|
||||
export let styleWindow = {};
|
||||
export let styleContent = {};
|
||||
export let setContext = baseSetContext;
|
||||
export let show: boolean = false;
|
||||
|
||||
let Component = null;
|
||||
let props = null;
|
||||
|
||||
let background;
|
||||
let wrap;
|
||||
let customStyleBg = {};
|
||||
let customStyleWindow = {};
|
||||
let customStyleContent = {};
|
||||
|
||||
let Callback = (props) => {};
|
||||
|
||||
$: Component,
|
||||
() => {
|
||||
show = !!Component;
|
||||
};
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
const camelCaseToDash = (str) =>
|
||||
str.replace(/([a-zA-Z])(?=[A-Z])/g, "$1-").toLowerCase();
|
||||
|
||||
const toCssString = (props) =>
|
||||
Object.keys(props).reduce(
|
||||
(str, key) => `${str}; ${camelCaseToDash(key)}: ${props[key]}`,
|
||||
""
|
||||
);
|
||||
|
||||
$: cssBg = toCssString(Object.assign({}, styleBg, customStyleBg));
|
||||
$: cssWindow = toCssString(
|
||||
Object.assign({}, styleWindow, customStyleWindow)
|
||||
);
|
||||
$: cssContent = toCssString(
|
||||
Object.assign({}, styleContent, customStyleContent)
|
||||
);
|
||||
|
||||
const open = (
|
||||
NewComponent,
|
||||
newProps = {},
|
||||
style = { bg: {}, window: {}, content: {} },
|
||||
newCallback = () => {}
|
||||
) => {
|
||||
Component = NewComponent;
|
||||
props = newProps;
|
||||
Callback = newCallback;
|
||||
customStyleBg = style.bg || {};
|
||||
customStyleWindow = style.window || {};
|
||||
customStyleContent = style.content || {};
|
||||
};
|
||||
|
||||
const close = () => {
|
||||
Callback(props);
|
||||
Component = null;
|
||||
props = null;
|
||||
customStyleBg = {};
|
||||
customStyleWindow = {};
|
||||
customStyleContent = {};
|
||||
};
|
||||
|
||||
const handleKeyup = ({ key }) => {
|
||||
if (closeOnEsc && Component && key === "Escape") {
|
||||
event.preventDefault();
|
||||
close();
|
||||
}
|
||||
};
|
||||
|
||||
const handleOuterClick = (event) => {
|
||||
if (
|
||||
closeOnOuterClick &&
|
||||
(event.target === background || event.target === wrap)
|
||||
) {
|
||||
event.preventDefault();
|
||||
close();
|
||||
}
|
||||
};
|
||||
|
||||
setContext(key, { open: show, close });
|
||||
</script>
|
||||
|
||||
<svelte:window on:keyup={handleKeyup} />
|
||||
|
||||
<div>
|
||||
{#if Component}
|
||||
<div
|
||||
class="bg"
|
||||
on:click={handleOuterClick}
|
||||
bind:this={background}
|
||||
transition:transitionBg={transitionBgProps}
|
||||
style={cssBg}
|
||||
>
|
||||
<div class="window-wrap" bind:this={wrap}>
|
||||
<div
|
||||
class="window"
|
||||
transition:transitionWindow={transitionWindowProps}
|
||||
style={cssWindow}
|
||||
>
|
||||
{#if closeButton}
|
||||
<button on:click={close} class="close" />
|
||||
{/if}
|
||||
<div class="content" style={cssContent}>
|
||||
<Component {...props} {close} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
<!--
|
||||
|
||||
** LICENSE **
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Fritz Lekschas
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
https://github.com/flekschas/svelte-simple-moda
|
||||
-->
|
||||
|
||||
<style>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.bg {
|
||||
position: fixed;
|
||||
z-index: 1000;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background: rgba(0, 0, 0, 0.66);
|
||||
}
|
||||
|
||||
.window-wrap {
|
||||
position: relative;
|
||||
margin: 2rem;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
.window {
|
||||
position: relative;
|
||||
width: 40rem;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
margin: 2rem auto;
|
||||
color: black;
|
||||
border-radius: 0.5rem;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.content {
|
||||
position: relative;
|
||||
padding: 1rem;
|
||||
max-height: calc(100vh - 4rem);
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.close {
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
z-index: 1000;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
border: 0;
|
||||
color: black;
|
||||
border-radius: 1.5rem;
|
||||
background: white;
|
||||
box-shadow: 0 0 0 1px black;
|
||||
transition: transform 0.2s cubic-bezier(0.25, 0.1, 0.25, 1),
|
||||
background 0.2s cubic-bezier(0.25, 0.1, 0.25, 1);
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
.close:before,
|
||||
.close:after {
|
||||
content: "";
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
width: 1rem;
|
||||
height: 1px;
|
||||
background: black;
|
||||
transform-origin: center;
|
||||
transition: height 0.2s cubic-bezier(0.25, 0.1, 0.25, 1),
|
||||
background 0.2s cubic-bezier(0.25, 0.1, 0.25, 1);
|
||||
}
|
||||
|
||||
.close:before {
|
||||
-webkit-transform: translate(0, -50%) rotate(45deg);
|
||||
-moz-transform: translate(0, -50%) rotate(45deg);
|
||||
transform: translate(0, -50%) rotate(45deg);
|
||||
left: 0.25rem;
|
||||
}
|
||||
|
||||
.close:after {
|
||||
-webkit-transform: translate(0, -50%) rotate(-45deg);
|
||||
-moz-transform: translate(0, -50%) rotate(-45deg);
|
||||
transform: translate(0, -50%) rotate(-45deg);
|
||||
left: 0.25rem;
|
||||
}
|
||||
|
||||
.close:hover {
|
||||
background: black;
|
||||
}
|
||||
|
||||
.close:hover:before,
|
||||
.close:hover:after {
|
||||
height: 2px;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.close:focus {
|
||||
border-color: #3399ff;
|
||||
box-shadow: 0 0 0 2px #3399ff;
|
||||
}
|
||||
|
||||
.close:active {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
|
||||
.close:hover,
|
||||
.close:focus,
|
||||
.close:active {
|
||||
outline: none;
|
||||
}
|
||||
</style>
|
||||
53
src/lib/Nav.svelte
Normal file
53
src/lib/Nav.svelte
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
<style>
|
||||
a {
|
||||
font-size: 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
nav {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
ul {
|
||||
display: flex;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
list-style: none;
|
||||
margin-top: 8px;
|
||||
margin-bottom: 8px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ul li a {
|
||||
text-decoration: none;
|
||||
transition: color 0.1s linear;
|
||||
padding: 4px 12px;
|
||||
}
|
||||
|
||||
ul li a:hover {
|
||||
border-bottom: 2px solid var(--line-color);
|
||||
}
|
||||
</style>
|
||||
|
||||
<header>
|
||||
<nav>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/">Quartznet</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/about">About</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/admin">Admin</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
56
src/lib/Sidebar.svelte
Normal file
56
src/lib/Sidebar.svelte
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
<script lang="ts">
|
||||
import { fly } from "svelte/transition";
|
||||
import Modal from "./Modal.svelte";
|
||||
|
||||
const click = () => {
|
||||
modal_show = true;
|
||||
show = false;
|
||||
};
|
||||
|
||||
function overlay_click(e: MouseEvent) {
|
||||
//@ts-ignore
|
||||
if ("close" in e.target.dataset) show = false;
|
||||
}
|
||||
|
||||
export let show = false;
|
||||
let modal_show = false;
|
||||
</script>
|
||||
|
||||
{#if show}
|
||||
<div class="overlay" data-close on:click={overlay_click}>
|
||||
<nav transition:fly={{ x: -250, opacity: 1 }}>
|
||||
<slot>
|
||||
<button on:click={click}>About</button>
|
||||
</slot>
|
||||
</nav>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<Modal bind:show={modal_show} />
|
||||
|
||||
<style>
|
||||
.overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0);
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
nav {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
padding: 2rem 1rem 0.6rem;
|
||||
border-right: 1px solid #aaa;
|
||||
background: #fff;
|
||||
overflow-y: auto;
|
||||
width: 20rem;
|
||||
}
|
||||
</style>
|
||||
255
src/lib/SortableList.svelte
Normal file
255
src/lib/SortableList.svelte
Normal file
|
|
@ -0,0 +1,255 @@
|
|||
<script lang="ts">
|
||||
//@ts-nocheck
|
||||
import { quintOut } from "svelte/easing";
|
||||
import { crossfade } from "svelte/transition";
|
||||
import { flip } from "svelte/animate";
|
||||
// FLIP ANIMATION
|
||||
const [send, receive] = crossfade({
|
||||
duration: (d) => Math.sqrt(d * 200),
|
||||
fallback(node, params) {
|
||||
const style = getComputedStyle(node);
|
||||
const transform = style.transform === "none" ? "" : style.transform;
|
||||
return {
|
||||
duration: 600,
|
||||
easing: quintOut,
|
||||
css: (t) => `
|
||||
transform: ${transform} scale(${t});
|
||||
opacity: ${t}
|
||||
`,
|
||||
};
|
||||
},
|
||||
});
|
||||
// DRAG AND DROP
|
||||
let isOver = false;
|
||||
let isDragging = false;
|
||||
const getDraggedParent = (node) => {
|
||||
return node.dataset && node.dataset.index
|
||||
? node.dataset
|
||||
: getDraggedParent(node.parentNode);
|
||||
};
|
||||
const click = (ev) => {
|
||||
dispatch("slotclick", {
|
||||
source: ev.target.parentElement.dataset.index,
|
||||
sourceItem: list[ev.target.parentElement.dataset.index],
|
||||
});
|
||||
};
|
||||
const start = (ev) => {
|
||||
isDragging = true;
|
||||
ev.dataTransfer.setData("source", ev.target.dataset.index);
|
||||
};
|
||||
const over = (ev) => {
|
||||
ev.preventDefault();
|
||||
let dragged = getDraggedParent(ev.target);
|
||||
if (isOver !== dragged.id) isOver = JSON.parse(dragged.id);
|
||||
};
|
||||
const leave = (ev) => {
|
||||
let dragged = getDraggedParent(ev.target);
|
||||
if (isOver === dragged.id) isOver = false;
|
||||
};
|
||||
const drop = (ev) => {
|
||||
isOver = false;
|
||||
ev.preventDefault();
|
||||
let dragged = getDraggedParent(ev.target);
|
||||
let from = ev.dataTransfer.getData("source");
|
||||
let to = dragged.index;
|
||||
reorder({ from, to });
|
||||
isDragging = false;
|
||||
};
|
||||
// DISPATCH REORDER
|
||||
import {
|
||||
createEventDispatcher,
|
||||
onMount,
|
||||
afterUpdate,
|
||||
beforeUpdate,
|
||||
tick,
|
||||
} from "svelte";
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
const reorder = ({ from, to }) => {
|
||||
let newList = [...list];
|
||||
let temp = newList[from];
|
||||
newList[from] = newList[to];
|
||||
newList[to] = temp;
|
||||
//newList[from] = [newList[to], (newList[to] = newList[from])][0];
|
||||
dispatch("sort", { data: newList, from: from, to: to });
|
||||
};
|
||||
// UTILS
|
||||
const getKey = (item) => (key ? item[key] : item);
|
||||
|
||||
type ListItem = {
|
||||
displayTileSize: number;
|
||||
endsGroup: boolean;
|
||||
startsNewGroup: boolean;
|
||||
};
|
||||
// PROPS
|
||||
export let list: ListItem[][];
|
||||
export let key;
|
||||
export let ulclass;
|
||||
|
||||
export let breakon: string | null = null;
|
||||
|
||||
export let breakComponent;
|
||||
// return `<><li class='${breakClass}' style='height:100%; grid-column: span 5;'><div style='width:100%;height:inherit;'></li>`;
|
||||
|
||||
// export let breakPosition = "beforebegin";
|
||||
let breakClass = "group-break";
|
||||
|
||||
const Elements = () => document.getElementsByClassName(key);
|
||||
|
||||
const removeOldBreaks = (parent) => {
|
||||
if (breakon == null) return;
|
||||
if (isDragging) return;
|
||||
|
||||
if (parent.length) {
|
||||
const old = parent[0].getElementsByClassName(breakClass);
|
||||
|
||||
for (let i = 0; i < old.length; i = 0) {
|
||||
old[i].remove(); // removing the node from the DOM also removes it from this array (WTF??)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let lastCollection;
|
||||
|
||||
let updatingBreakPoints = false;
|
||||
|
||||
const updateBreak = () => {
|
||||
if (isDragging || updatingBreakPoints) return;
|
||||
if (breakon == null) return;
|
||||
|
||||
try {
|
||||
updatingBreakPoints = true;
|
||||
|
||||
let breaks = 0;
|
||||
let last = null;
|
||||
let carry = 0;
|
||||
let elements = Elements();
|
||||
|
||||
for (let x = 0; x < list.length; x++) {
|
||||
if (last != null) {
|
||||
if (list[x][breakon] > last) {
|
||||
if (elements.length) {
|
||||
let targetIndex = x + breaks;
|
||||
|
||||
if (
|
||||
list.length !==
|
||||
elements[0].children.length - breaks
|
||||
) {
|
||||
if (lastCollection)
|
||||
if (lastCollection.length)
|
||||
targetIndex += lastCollection.length;
|
||||
}
|
||||
// elements[0].children[targetIndex].insertAdjacentHTML(breakPosition, breakHtml(carry + 1));
|
||||
breaks++;
|
||||
carry = 0;
|
||||
}
|
||||
} else {
|
||||
carry++;
|
||||
}
|
||||
}
|
||||
|
||||
last = list[x][breakon] > last ? list[x][breakon] : last;
|
||||
}
|
||||
|
||||
lastCollection = list;
|
||||
} finally {
|
||||
updatingBreakPoints = false;
|
||||
}
|
||||
};
|
||||
|
||||
//beforeUpdate(() => {updateBreak()});
|
||||
|
||||
let updating = false;
|
||||
|
||||
afterUpdate(async () => {
|
||||
if (isDragging) return;
|
||||
if (breakon == null) return;
|
||||
|
||||
updating = true;
|
||||
removeOldBreaks(Elements());
|
||||
await tick();
|
||||
updateBreak();
|
||||
|
||||
updating = false;
|
||||
});
|
||||
|
||||
const getClass = () => {
|
||||
let ret = "";
|
||||
if (key) ret += ` ${key}`;
|
||||
if (ulclass) ret += ` ${ulclass}`;
|
||||
|
||||
return ret;
|
||||
};
|
||||
</script>
|
||||
|
||||
{#if list && list.length}
|
||||
<ul class={getClass()}>
|
||||
{#each list as item, index (getKey(item))}
|
||||
<li
|
||||
class:small={item.displayTileSize === 2}
|
||||
class:medium={item.displayTileSize === 3}
|
||||
class:large={item.displayTileSize === 4}
|
||||
class:startsNewGroup={item.startsNewGroup}
|
||||
class:endsGroup={item.endsGroup}
|
||||
data-index={index}
|
||||
data-item={JSON.stringify(item)}
|
||||
data-id={JSON.stringify(getKey(item))}
|
||||
draggable="true"
|
||||
on:dragstart={start}
|
||||
on:dragover={over}
|
||||
on:dragleave={leave}
|
||||
on:drop={drop}
|
||||
on:click={click}
|
||||
in:receive={{ key: getKey(item) }}
|
||||
out:send={{ key: getKey(item) }}
|
||||
animate:flip={{ duration: 300 }}
|
||||
class:over={getKey(item) === isOver}
|
||||
>
|
||||
<slot {item} {index}>
|
||||
<p>{getKey(item)}</p>
|
||||
</slot>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
ul {
|
||||
display: inline-grid;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
li {
|
||||
display: inline-block;
|
||||
border: 2px dotted transparent;
|
||||
transition: border 0.1s linear;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0.5%;
|
||||
}
|
||||
|
||||
.startsNewGroup {
|
||||
grid-column-start: 1;
|
||||
}
|
||||
|
||||
.small {
|
||||
grid-column-end: span 1;
|
||||
grid-row: span 1;
|
||||
}
|
||||
|
||||
.medium {
|
||||
grid-column-end: span 2;
|
||||
grid-row: span 1;
|
||||
}
|
||||
|
||||
.large {
|
||||
grid-column-end: span 2;
|
||||
/*grid-row: span 2; large buttons are double height, but they also push the row below them down (they dont fit around eachother like css-grid) */
|
||||
}
|
||||
|
||||
.over {
|
||||
border-color: rgba(48, 12, 200, 0.2);
|
||||
}
|
||||
</style>
|
||||
43
src/lib/Thumbnail.svelte
Normal file
43
src/lib/Thumbnail.svelte
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<script lang="ts">
|
||||
export let name: string;
|
||||
export let link: string;
|
||||
export let img = "https://placehold.it/600x300";
|
||||
export let size = { width: 600, height: 300 };
|
||||
export let description: string;
|
||||
</script>
|
||||
|
||||
<div class="card">
|
||||
<div class="back">
|
||||
<a href={link}>
|
||||
<img src={img} alt={name} width={size.width} height={size.height} />
|
||||
</a>
|
||||
</div>
|
||||
<div class="description">
|
||||
<h2>{name}</h2>
|
||||
<p>{description}</p>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
|
||||
<style>
|
||||
.card {
|
||||
display: flex;
|
||||
gap: 2.5rem;
|
||||
}
|
||||
|
||||
.back a img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.description {
|
||||
width: 500px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 978px) {
|
||||
.card {
|
||||
flex-wrap: wrap;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
16
src/lib/UserManager.svelte
Normal file
16
src/lib/UserManager.svelte
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<script>
|
||||
export const name = "%USER%";
|
||||
export let icon;
|
||||
export let onClick;
|
||||
</script>
|
||||
|
||||
<style>
|
||||
img {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
<a href="/admin">Admin</a>
|
||||
Loading…
Reference in a new issue