Merge branch 'develop'
This commit is contained in:
commit
d76a227c5c
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "swaf",
|
"name": "swaf",
|
||||||
"version": "0.24.5",
|
"version": "0.24.6",
|
||||||
"description": "Structure Web Application Framework.",
|
"description": "Structure Web Application Framework.",
|
||||||
"repository": "https://eternae.ink/ashpie/swaf",
|
"repository": "https://eternae.ink/ashpie/swaf",
|
||||||
"author": "Alice Gaudon <alice@gaudon.pro>",
|
"author": "Alice Gaudon <alice@gaudon.pro>",
|
||||||
@ -33,7 +33,6 @@
|
|||||||
"@types/cookie-parser": "^1.4.2",
|
"@types/cookie-parser": "^1.4.2",
|
||||||
"@types/express": "^4.17.6",
|
"@types/express": "^4.17.6",
|
||||||
"@types/express-session": "^1.17.0",
|
"@types/express-session": "^1.17.0",
|
||||||
"@types/feather-icons": "^4.7.0",
|
|
||||||
"@types/formidable": "^2.0.0",
|
"@types/formidable": "^2.0.0",
|
||||||
"@types/geoip-lite": "^1.1.31",
|
"@types/geoip-lite": "^1.1.31",
|
||||||
"@types/jest": "^27.0.2",
|
"@types/jest": "^27.0.2",
|
||||||
@ -56,7 +55,6 @@
|
|||||||
"eslint-plugin-node": "^11.1.0",
|
"eslint-plugin-node": "^11.1.0",
|
||||||
"eslint-plugin-simple-import-sort": "^7.0.0",
|
"eslint-plugin-simple-import-sort": "^7.0.0",
|
||||||
"eslint-plugin-svelte3": "^3.1.2",
|
"eslint-plugin-svelte3": "^3.1.2",
|
||||||
"feather-icons": "^4.28.0",
|
|
||||||
"jest": "^27.3.1",
|
"jest": "^27.3.1",
|
||||||
"jest-resolve": "^27.3.1",
|
"jest-resolve": "^27.3.1",
|
||||||
"jest-ts-webcompat-resolver": "^1.0.0",
|
"jest-ts-webcompat-resolver": "^1.0.0",
|
||||||
@ -84,6 +82,7 @@
|
|||||||
"express-session": "^1.17.1",
|
"express-session": "^1.17.1",
|
||||||
"formidable": "^2.0.1",
|
"formidable": "^2.0.1",
|
||||||
"geoip-lite": "^1.4.2",
|
"geoip-lite": "^1.4.2",
|
||||||
|
"lucide": "^0.16.17",
|
||||||
"mjml": "^4.6.2",
|
"mjml": "^4.6.2",
|
||||||
"mysql": "^2.18.1",
|
"mysql": "^2.18.1",
|
||||||
"nanoid": "^3.1.20",
|
"nanoid": "^3.1.20",
|
||||||
|
@ -27,6 +27,22 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Buttons
|
||||||
|
button, .button {
|
||||||
|
&:not(.bold) {
|
||||||
|
--background-color: var(--surface);
|
||||||
|
|
||||||
|
&:hover::after {
|
||||||
|
--background-color: var(--on-surface);
|
||||||
|
|
||||||
|
:global(&) {
|
||||||
|
--background-color: var(--on-surface);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// States modifiers
|
// States modifiers
|
||||||
.primary:not(.bold) {
|
.primary:not(.bold) {
|
||||||
--color: var(--primary-on-surface);
|
--color: var(--primary-on-surface);
|
||||||
@ -49,19 +65,6 @@
|
|||||||
--background-color: var(--surface);
|
--background-color: var(--surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Buttons
|
|
||||||
button {
|
|
||||||
--background-color: var(--surface);
|
|
||||||
}
|
|
||||||
button:hover::after {
|
|
||||||
background-color: var(--on-surface);
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(button:hover::after) {
|
|
||||||
background-color: var(--on-surface);
|
|
||||||
}
|
|
||||||
|
|
||||||
@if ($shadowStrength > 0) {
|
@if ($shadowStrength > 0) {
|
||||||
box-shadow: 0 #{$shadowStrength}px #{$shadowStrength}px #00000045;
|
box-shadow: 0 #{$shadowStrength}px #{$shadowStrength}px #00000045;
|
||||||
}
|
}
|
||||||
|
@ -155,7 +155,7 @@ h1 {
|
|||||||
a {
|
a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
|
||||||
.feather.feather-external-link { //todo add js
|
.icon.lucide-external-link { //todo add js
|
||||||
--icon-size: 16px;
|
--icon-size: 16px;
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
margin-top: -3px;
|
margin-top: -3px;
|
||||||
@ -239,12 +239,12 @@ button, .button {
|
|||||||
border: 0;
|
border: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.feather {
|
.icon {
|
||||||
--icon-size: 16px;
|
--icon-size: 16px;
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.feather.last {
|
.icon.last {
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
margin: 16px 8px;
|
margin: 16px 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
> .feather:first-child {
|
> .icon:first-child {
|
||||||
--icon-size: 24px;
|
--icon-size: 24px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
opacity: 0.2;
|
opacity: 0.2;
|
||||||
@ -35,7 +35,7 @@
|
|||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
|
|
||||||
.feather {
|
.icon {
|
||||||
--icon-size: 24px;
|
--icon-size: 24px;
|
||||||
margin: 0 16px 0 0;
|
margin: 0 16px 0 0;
|
||||||
opacity: 0.2;
|
opacity: 0.2;
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
import feather from "feather-icons";
|
|
||||||
|
|
||||||
let alreadyReplaced = false;
|
|
||||||
|
|
||||||
export function replaceIcons(once: boolean = true): void {
|
|
||||||
if (!once || !alreadyReplaced) {
|
|
||||||
alreadyReplaced = true;
|
|
||||||
feather.replace();
|
|
||||||
}
|
|
||||||
}
|
|
11
src/assets/ts/icons.ts
Normal file
11
src/assets/ts/icons.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import {createIcons, icons} from "lucide";
|
||||||
|
|
||||||
|
let hasAlreadyReplacedIcons = false;
|
||||||
|
|
||||||
|
export function replaceIcons(once: boolean): void {
|
||||||
|
if (!once || !hasAlreadyReplacedIcons) {
|
||||||
|
console.log('Create icons...');
|
||||||
|
createIcons({icons});
|
||||||
|
hasAlreadyReplacedIcons = true;
|
||||||
|
}
|
||||||
|
}
|
@ -4,13 +4,14 @@
|
|||||||
import Form from "../../utils/Form.svelte";
|
import Form from "../../utils/Form.svelte";
|
||||||
import Field from "../../utils/Field.svelte";
|
import Field from "../../utils/Field.svelte";
|
||||||
import {hasRoute, route} from "../../../../common/Routing";
|
import {hasRoute, route} from "../../../../common/Routing";
|
||||||
|
import Icon from "../../utils/Icon.svelte";
|
||||||
|
|
||||||
let newName = '';
|
let newName = '';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if hasRoute('change-name')}
|
{#if hasRoute('change-name')}
|
||||||
<section class="panel">
|
<section class="panel">
|
||||||
<h2><i data-feather="key"></i> Change name</h2>
|
<h2><Icon name="key"/> Change name</h2>
|
||||||
|
|
||||||
|
|
||||||
{#if $locals.can_change_name}
|
{#if $locals.can_change_name}
|
||||||
|
@ -3,13 +3,14 @@
|
|||||||
import Form from "../../utils/Form.svelte";
|
import Form from "../../utils/Form.svelte";
|
||||||
import Field from "../../utils/Field.svelte";
|
import Field from "../../utils/Field.svelte";
|
||||||
import {hasRoute, route} from "../../../../common/Routing";
|
import {hasRoute, route} from "../../../../common/Routing";
|
||||||
|
import Icon from "../../utils/Icon.svelte";
|
||||||
|
|
||||||
let removePasswordMode = false;
|
let removePasswordMode = false;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if hasRoute('remove-password', 'change-password')}
|
{#if hasRoute('remove-password', 'change-password')}
|
||||||
<section class="panel">
|
<section class="panel">
|
||||||
<h2><i data-feather="key"></i> {$locals.has_password ? 'Change' : 'Set'} password</h2>
|
<h2><Icon name="key"/> {$locals.has_password ? 'Change' : 'Set'} password</h2>
|
||||||
|
|
||||||
{#if removePasswordMode}
|
{#if removePasswordMode}
|
||||||
<Form action={route('remove-password')}
|
<Form action={route('remove-password')}
|
||||||
|
@ -1,22 +1,23 @@
|
|||||||
<script>
|
<script>
|
||||||
import {locals} from "../../../ts/stores";
|
import {locals} from "../../../ts/stores";
|
||||||
import BaseLayout from "../../layouts/BaseLayout.svelte";
|
import BaseTemplate from "../../templates/BaseTemplate.svelte";
|
||||||
import Message from "../../components/Message.svelte";
|
import Message from "../../components/Message.svelte";
|
||||||
import NamePanel from "./NamePanel.svelte";
|
import NamePanel from "./NamePanel.svelte";
|
||||||
import PasswordPanel from "./PasswordPanel.svelte";
|
import PasswordPanel from "./PasswordPanel.svelte";
|
||||||
import Form from "../../utils/Form.svelte";
|
import Form from "../../utils/Form.svelte";
|
||||||
import Field from "../../utils/Field.svelte";
|
import Field from "../../utils/Field.svelte";
|
||||||
import {hasRoute, route} from "../../../../common/Routing";
|
import {hasRoute, route} from "../../../../common/Routing";
|
||||||
|
import Icon from "../../utils/Icon.svelte";
|
||||||
|
|
||||||
const mainEmail = $locals.main_email?.email;
|
const mainEmail = $locals.main_email?.email;
|
||||||
const personalInfoFields = $locals.user_personal_info_fields || [];
|
const personalInfoFields = $locals.user_personal_info_fields || [];
|
||||||
const emails = $locals.emails || [];
|
const emails = $locals.emails || [];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<BaseLayout title="Account" description="Manage your account settings and data.">
|
<BaseTemplate title="Account" description="Manage your account settings and data.">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="panel">
|
<div class="panel">
|
||||||
<h2><i data-feather="user"></i> Personal information</h2>
|
<h2><Icon name="user"/> Personal information</h2>
|
||||||
|
|
||||||
{#if $locals.display_email_warning && $locals.emails.length <= 0}
|
{#if $locals.display_email_warning && $locals.emails.length <= 0}
|
||||||
<Message type="warning" content="To avoid losing access to your account, please add an email address."/>
|
<Message type="warning" content="To avoid losing access to your account, please add an email address."/>
|
||||||
@ -40,7 +41,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<section class="panel">
|
<section class="panel">
|
||||||
<h2 id="emails"><i data-feather="shield"></i> Email addresses</h2>
|
<h2 id="emails"><Icon name="shield"/> Email addresses</h2>
|
||||||
|
|
||||||
<div class="data-table-container">
|
<div class="data-table-container">
|
||||||
<table class="data-table">
|
<table class="data-table">
|
||||||
@ -100,4 +101,4 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</BaseLayout>
|
</BaseTemplate>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {locals} from "../../ts/stores.js";
|
import {locals} from "../../ts/stores.js";
|
||||||
import BaseLayout from "../layouts/BaseLayout.svelte";
|
import BaseTemplate from "../templates/BaseTemplate.svelte";
|
||||||
import Form from "../utils/Form.svelte";
|
import Form from "../utils/Form.svelte";
|
||||||
import Field from "../utils/Field.svelte";
|
import Field from "../utils/Field.svelte";
|
||||||
import Icon from "../utils/Icon.svelte";
|
import Icon from "../utils/Icon.svelte";
|
||||||
@ -18,14 +18,14 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<BaseLayout title="Authentication / Registration"
|
<BaseTemplate title="Authentication / Registration"
|
||||||
description="Join {$locals.app.name} and share your files!"
|
description="Join {$locals.app.name} and share your files!"
|
||||||
h1="Authentication and registration">
|
h1="Authentication and registration">
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
{#if hasRoute('login')}
|
{#if hasRoute('login')}
|
||||||
<section class="panel">
|
<section class="panel">
|
||||||
<h2><i data-feather="log-in"></i> Log in</h2>
|
<h2><Icon name="log-in"/> Log in</h2>
|
||||||
|
|
||||||
<Form action={route('login') + queryStr} submitText="Authenticate" submitIcon="log-in">
|
<Form action={route('login') + queryStr} submitText="Authenticate" submitIcon="log-in">
|
||||||
<Field type="text" name="identifier" value={$locals.query?.identifier} icon="at-sign"
|
<Field type="text" name="identifier" value={$locals.query?.identifier} icon="at-sign"
|
||||||
@ -45,7 +45,7 @@
|
|||||||
|
|
||||||
{#if hasRoute('register')}
|
{#if hasRoute('register')}
|
||||||
<section class="panel">
|
<section class="panel">
|
||||||
<h2><i data-feather="user-plus"></i> Register</h2>
|
<h2><Icon name="user-plus"/> Register</h2>
|
||||||
|
|
||||||
<Form action={route('register') + queryStr} submitText="Register" submitIcon="check">
|
<Form action={route('register') + queryStr} submitText="Register" submitIcon="check">
|
||||||
<Field type="hidden" name="auth_method" value={registerUsingMagicLink ? 'magic_link': 'password'}/>
|
<Field type="hidden" name="auth_method" value={registerUsingMagicLink ? 'magic_link': 'password'}/>
|
||||||
@ -74,4 +74,4 @@
|
|||||||
</section>
|
</section>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</BaseLayout>
|
</BaseTemplate>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {locals} from "../../ts/stores";
|
import {locals} from "../../ts/stores";
|
||||||
import BaseLayout from "../layouts/BaseLayout.svelte";
|
import BaseTemplate from "../templates/BaseTemplate.svelte";
|
||||||
import Pagination from "../components/Pagination.svelte";
|
import Pagination from "../components/Pagination.svelte";
|
||||||
import Form from "../utils/Form.svelte";
|
import Form from "../utils/Form.svelte";
|
||||||
import Field from "../utils/Field.svelte";
|
import Field from "../utils/Field.svelte";
|
||||||
@ -16,7 +16,7 @@
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<BaseLayout title="{$locals.app.name} - Review accounts" h1={false}>
|
<BaseTemplate title="{$locals.app.name} - Review accounts" h1={false}>
|
||||||
{#if hasRoute('backend')}
|
{#if hasRoute('backend')}
|
||||||
<Breadcrumb currentPageTitle="Accounts pending review" pages={[
|
<Breadcrumb currentPageTitle="Accounts pending review" pages={[
|
||||||
{link: route('backend'), title:'Backend'},
|
{link: route('backend'), title:'Backend'},
|
||||||
@ -78,4 +78,4 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Pagination pagination={$locals.pagination} routeName="accounts-approval" contextSize="3" />
|
<Pagination pagination={$locals.pagination} routeName="accounts-approval" contextSize="3" />
|
||||||
</BaseLayout>
|
</BaseTemplate>
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {locals} from "../../ts/stores.js";
|
import {locals} from "../../ts/stores.js";
|
||||||
import BaseLayout from "../layouts/BaseLayout.svelte";
|
import BaseTemplate from "../templates/BaseTemplate.svelte";
|
||||||
import Breadcrumb from "../components/Breadcrumb.svelte";
|
import Breadcrumb from "../components/Breadcrumb.svelte";
|
||||||
|
import Icon from "../utils/Icon.svelte";
|
||||||
|
|
||||||
const menu = $locals.menu || [];
|
const menu = $locals.menu || [];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<BaseLayout title="{$locals.app.name} backend" h1={false}>
|
<BaseTemplate title="{$locals.app.name} backend" h1={false}>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<Breadcrumb currentPageTitle="Backend"/>
|
<Breadcrumb currentPageTitle="Backend"/>
|
||||||
|
|
||||||
@ -19,7 +20,7 @@
|
|||||||
<li>
|
<li>
|
||||||
<a href={element.link}>
|
<a href={element.link}>
|
||||||
{#if element.display_icon !== null}
|
{#if element.display_icon !== null}
|
||||||
<i data-feather={element.display_icon}></i>
|
<Icon name={element.display_icon}/>
|
||||||
{/if}
|
{/if}
|
||||||
{element.display_string}
|
{element.display_string}
|
||||||
</a>
|
</a>
|
||||||
@ -29,4 +30,4 @@
|
|||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</BaseLayout>
|
</BaseTemplate>
|
||||||
|
95
src/assets/views/components/CopyableText.svelte
Normal file
95
src/assets/views/components/CopyableText.svelte
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import Icon from "../utils/Icon.svelte";
|
||||||
|
import { fade } from "svelte/transition";
|
||||||
|
|
||||||
|
export let title: string;
|
||||||
|
export let content: string;
|
||||||
|
|
||||||
|
let contentNode: HTMLElement;
|
||||||
|
let copiedOverlay: HTMLElement;
|
||||||
|
|
||||||
|
|
||||||
|
function selectAll() {
|
||||||
|
const selection = window.getSelection();
|
||||||
|
if (contentNode && selection) {
|
||||||
|
selection.selectAllChildren(contentNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function copy() {
|
||||||
|
const selection = window.getSelection();
|
||||||
|
if (contentNode && selection) {
|
||||||
|
selectAll();
|
||||||
|
navigator.clipboard.writeText(contentNode.innerText);
|
||||||
|
showOverlay();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let showCopiedOverlay = false;
|
||||||
|
function showOverlay() {
|
||||||
|
showCopiedOverlay = true;
|
||||||
|
}
|
||||||
|
function releaseOverlay() {
|
||||||
|
showCopiedOverlay = false;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import "../../scss/helpers";
|
||||||
|
.copyable-text {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
margin: 8px;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
border-radius: 5px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
width: 0;
|
||||||
|
flex-grow: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-button {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
|
||||||
|
:global(.icon) {
|
||||||
|
--icon-size: 20px;
|
||||||
|
margin: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.copied-overlay {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 8px;
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
background-color: var(--success);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="copyable-text panel">
|
||||||
|
<div class="title">{title}</div>
|
||||||
|
<div class="content" bind:this={contentNode} on:click={selectAll}>{content}</div>
|
||||||
|
<button class="bold copy-button" on:click={copy}><Icon name="copy"/></button>
|
||||||
|
|
||||||
|
{#if showCopiedOverlay}
|
||||||
|
<div class="copied-overlay" bind:this={copiedOverlay} out:fade on:mouseleave={releaseOverlay}>Copied!</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
@ -45,7 +45,7 @@
|
|||||||
|
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
|
|
||||||
:global(.feather) {
|
:global(.icon) {
|
||||||
--icon-size: 24px;
|
--icon-size: 24px;
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
@ -77,7 +77,7 @@
|
|||||||
&-discreet {
|
&-discreet {
|
||||||
color: var(--on-surface);
|
color: var(--on-surface);
|
||||||
|
|
||||||
.feather {
|
.icon {
|
||||||
--icon-size: 20px;
|
--icon-size: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -94,7 +94,7 @@
|
|||||||
|
|
||||||
border: 0;
|
border: 0;
|
||||||
|
|
||||||
:global(.feather) {
|
:global(.icon) {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,7 +96,7 @@
|
|||||||
|
|
||||||
border-radius: $headerHeight;
|
border-radius: $headerHeight;
|
||||||
|
|
||||||
:global(.feather) {
|
:global(.icon) {
|
||||||
--icon-size: 28px;
|
--icon-size: 28px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.feather) {
|
:global(.icon) {
|
||||||
--icon-size: 16px;
|
--icon-size: 16px;
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<script>
|
<script>
|
||||||
import Error from "./Error.svelte";
|
import ErrorTemplate from "../templates/ErrorTemplate.svelte";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Error code={400} message="Bad request." />
|
<ErrorTemplate code={400} message="Bad request." />
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<script>
|
<script>
|
||||||
import Error from "./Error.svelte";
|
import ErrorTemplate from "../templates/ErrorTemplate.svelte";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Error code={401} message="Unauthorized." />
|
<ErrorTemplate code={401} message="Unauthorized." />
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<script>
|
<script>
|
||||||
import Error from "./Error.svelte";
|
import ErrorTemplate from "../templates/ErrorTemplate.svelte";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Error code={403} message="Forbidden" />
|
<ErrorTemplate code={403} message="Forbidden" />
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<script>
|
<script>
|
||||||
import Error from "./Error.svelte";
|
import ErrorTemplate from "../templates/ErrorTemplate.svelte";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Error code={404} message="Page not found." />
|
<ErrorTemplate code={404} message="Page not found." />
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<script>
|
<script>
|
||||||
import Error from "./Error.svelte";
|
import ErrorTemplate from "../templates/ErrorTemplate.svelte";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Error code={429} message="Too many requests." />
|
<ErrorTemplate code={429} message="Too many requests." />
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<script>
|
<script>
|
||||||
import Error from "./Error.svelte";
|
import ErrorTemplate from "../templates/ErrorTemplate.svelte";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Error code={500} message="Internal server error." />
|
<ErrorTemplate code={500} message="Internal server error." />
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<script>
|
<script>
|
||||||
import Error from "./Error.svelte";
|
import ErrorTemplate from "../templates/ErrorTemplate.svelte";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Error code={503} message="Service unavailable." />
|
<ErrorTemplate code={503} message="Service unavailable." />
|
||||||
|
@ -1,47 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import {locals} from "../../ts/stores.js";
|
|
||||||
|
|
||||||
const previousUrl = $locals.getPreviousUrl();
|
|
||||||
|
|
||||||
export let code;
|
|
||||||
code = $locals.error_code || code;
|
|
||||||
|
|
||||||
export let message;
|
|
||||||
message = $locals.error_message || message;
|
|
||||||
|
|
||||||
export let instructions;
|
|
||||||
instructions = $locals.error_instructions || instructions;
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<svelte:head>
|
|
||||||
<title>{code + ' - ' + message}</title>
|
|
||||||
<link rel="stylesheet" href="/css/error.css">
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
<div class="logo"><a href="/">{$locals.app.name}</a></div>
|
|
||||||
|
|
||||||
<main>
|
|
||||||
<!--TODO: flash messages-->
|
|
||||||
|
|
||||||
<div class="error-code">{code}</div>
|
|
||||||
<div class="error-message">{message}</div>
|
|
||||||
{#if instructions}
|
|
||||||
<div class="error-instructions">{@html instructions}</div>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<nav>
|
|
||||||
{#if previousUrl && previousUrl !== '/' && previousUrl !== $locals.url}
|
|
||||||
<a href={previousUrl} class="button"><i data-feather="arrow-left"></i> Go back</a>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<a href="/" class="button"><i data-feather="home"></i> Go to homepage</a>
|
|
||||||
</nav>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<div class="contact">
|
|
||||||
<p>Error ID: {$locals.error_id || 'Request has no indentifier.'}</p>
|
|
||||||
<p>
|
|
||||||
If you think this isn't right, please contact us with the above error ID at
|
|
||||||
<a href="mailto:{$locals.app.contact_email}">{$locals.app.contact_email}</a>.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
@ -1,12 +1,20 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {locals} from "../ts/stores";
|
import {locals} from "../ts/stores";
|
||||||
import {route, hasRoute, hasAnyRoute} from "../../common/Routing";
|
import {route, hasRoute, hasAnyRoute} from "../../common/Routing";
|
||||||
import BaseLayout from "./layouts/BaseLayout.svelte";
|
import BaseTemplate from "./templates/BaseTemplate.svelte";
|
||||||
|
import {onMount} from "svelte";
|
||||||
|
|
||||||
|
let randomTitleSWord = 'Svelte';
|
||||||
|
const possibleSWords = ['Svelte', 'Simple', 'Scalable', 'Super', 'Structure', 'Satisfying'];
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
randomTitleSWord = possibleSWords[Math.floor(Math.random() * possibleSWords.length)];
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<BaseLayout title="{$locals.app.name}" h1={false}>
|
<BaseTemplate title="{$locals.app.name}" h1={false}>
|
||||||
<div class="panel">
|
<div class="panel">
|
||||||
<h1>swaf - Svelte Web Application Framework</h1>
|
<h1>swaf - {randomTitleSWord} Web Application Framework</h1>
|
||||||
<p>Welcome to {$locals.app.name}!</p>
|
<p>Welcome to {$locals.app.name}!</p>
|
||||||
|
|
||||||
{#if hasAnyRoute('tests', 'design')}
|
{#if hasAnyRoute('tests', 'design')}
|
||||||
@ -22,4 +30,4 @@
|
|||||||
</nav>
|
</nav>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</BaseLayout>
|
</BaseTemplate>
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
<script>
|
<script>
|
||||||
import {locals} from "../ts/stores";
|
import {locals} from "../ts/stores";
|
||||||
import BaseLayout from "./layouts/BaseLayout.svelte";
|
import BaseTemplate from "./templates/BaseTemplate.svelte";
|
||||||
import Message from "./components/Message.svelte";
|
import Message from "./components/Message.svelte";
|
||||||
|
|
||||||
const actionType = $locals.magicLink?.action_type;
|
const actionType = $locals.magicLink?.action_type;
|
||||||
const h1 = 'Magic Link' + (actionType ? (' - ' + actionType) : '');
|
const h1 = 'Magic Link' + (actionType ? (' - ' + actionType) : '');
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<BaseLayout title="{$locals.app.name} {h1}" {h1}>
|
<BaseTemplate title="{$locals.app.name} {h1}" {h1}>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="panel">
|
<div class="panel">
|
||||||
{#if $locals.err}
|
{#if $locals.err}
|
||||||
@ -18,4 +18,4 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</BaseLayout>
|
</BaseTemplate>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {locals} from "../ts/stores.js";
|
import {locals} from "../ts/stores.js";
|
||||||
import BaseLayout from "./layouts/BaseLayout.svelte";
|
import BaseTemplate from "./templates/BaseTemplate.svelte";
|
||||||
import Message from "./components/Message.svelte";
|
import Message from "./components/Message.svelte";
|
||||||
import WebsocketClient from "../ts/WebsocketClient.js";
|
import WebsocketClient from "../ts/WebsocketClient.js";
|
||||||
import {Time} from "../../common/Time.js";
|
import {Time} from "../../common/Time.js";
|
||||||
@ -37,7 +37,7 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<BaseLayout h1="Authentication lobby" title="{$locals.app.name} authentication lobby">
|
<BaseTemplate h1="Authentication lobby" title="{$locals.app.name} authentication lobby">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="panel">
|
<div class="panel">
|
||||||
<Message type="success" sticky
|
<Message type="success" sticky
|
||||||
@ -48,4 +48,4 @@
|
|||||||
<p class="center">Waiting for you to open the link...</p>
|
<p class="center">Waiting for you to open the link...</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</BaseLayout>
|
</BaseTemplate>
|
||||||
|
32
src/assets/views/scripts/ExternalLinkIcons.svelte
Normal file
32
src/assets/views/scripts/ExternalLinkIcons.svelte
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {onMount} from "svelte";
|
||||||
|
import Icon from "../utils/Icon.svelte";
|
||||||
|
|
||||||
|
let iconTemplate: HTMLTemplateElement;
|
||||||
|
|
||||||
|
function addExternalLinkIcons(): void {
|
||||||
|
console.log('Add icons to external links...');
|
||||||
|
|
||||||
|
const iconElement = iconTemplate.childNodes.item(0);
|
||||||
|
|
||||||
|
document.querySelectorAll('a[target="_blank"]').forEach(el => {
|
||||||
|
if (!el.classList.contains('no-icon')) {
|
||||||
|
el.appendChild(iconElement.cloneNode(true));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
addExternalLinkIcons();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
div {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div bind:this={iconTemplate}>
|
||||||
|
<Icon name="external-link"/>
|
||||||
|
</div>
|
@ -2,6 +2,7 @@
|
|||||||
import FlashMessages from "../components/FlashMessages.svelte";
|
import FlashMessages from "../components/FlashMessages.svelte";
|
||||||
import BaseFooter from "./base/BaseFooter.svelte";
|
import BaseFooter from "./base/BaseFooter.svelte";
|
||||||
import BaseHeader from "./base/BaseHeader.svelte";
|
import BaseHeader from "./base/BaseHeader.svelte";
|
||||||
|
import CommonScripts from "./CommonScripts.svelte";
|
||||||
|
|
||||||
export let title: string;
|
export let title: string;
|
||||||
export let h1: string = title;
|
export let h1: string = title;
|
||||||
@ -9,6 +10,8 @@
|
|||||||
export let refresh_after: number | undefined = undefined;
|
export let refresh_after: number | undefined = undefined;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<CommonScripts/>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import "../../scss/vars";
|
@import "../../scss/vars";
|
||||||
@import "../../scss/helpers";
|
@import "../../scss/helpers";
|
5
src/assets/views/templates/CommonScripts.svelte
Normal file
5
src/assets/views/templates/CommonScripts.svelte
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<script>
|
||||||
|
import ExternalLinkIcons from "../scripts/ExternalLinkIcons.svelte";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ExternalLinkIcons/>
|
145
src/assets/views/templates/ErrorTemplate.svelte
Normal file
145
src/assets/views/templates/ErrorTemplate.svelte
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {locals} from "../../ts/stores.js";
|
||||||
|
import FlashMessages from "../components/FlashMessages.svelte";
|
||||||
|
import Icon from "../utils/Icon.svelte";
|
||||||
|
import CommonScripts from "./CommonScripts.svelte";
|
||||||
|
|
||||||
|
const previousUrl = $locals.getPreviousUrl();
|
||||||
|
|
||||||
|
export let code;
|
||||||
|
code = $locals.error_code || code;
|
||||||
|
|
||||||
|
export let message;
|
||||||
|
message = $locals.error_message || message;
|
||||||
|
|
||||||
|
export let instructions;
|
||||||
|
instructions = $locals.error_instructions || instructions;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<CommonScripts/>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>{code + ' - ' + message}</title>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="/css/layout.css">
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<div class="logo"><a href="/">{$locals.app.name}</a></div>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<FlashMessages/>
|
||||||
|
|
||||||
|
<div class="error-code">{code}</div>
|
||||||
|
<div class="error-message">{message}</div>
|
||||||
|
{#if instructions}
|
||||||
|
<div class="error-instructions">{@html instructions}</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<nav>
|
||||||
|
{#if previousUrl && previousUrl !== '/' && previousUrl !== $locals.url}
|
||||||
|
<a href={previousUrl} class="button bold"><Icon name="arrow-left"/> Go back</a>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<a href="/" class="button"><Icon name="home"/> Go to homepage</a>
|
||||||
|
</nav>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<div class="contact">
|
||||||
|
<p>Error ID: {$locals.error_id || 'Request has no indentifier.'}</p>
|
||||||
|
<p>
|
||||||
|
If you think this isn't right, please contact us with the above error ID at
|
||||||
|
<a href="mailto:{$locals.app.contact_email}">{$locals.app.contact_email}</a>.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
header, footer {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.messages {
|
||||||
|
margin-bottom: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-code {
|
||||||
|
font-size: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-message {
|
||||||
|
font-size: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-instructions {
|
||||||
|
margin-top: 32px;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav {
|
||||||
|
margin-top: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "Oops";
|
||||||
|
position: absolute;
|
||||||
|
z-index: -1;
|
||||||
|
|
||||||
|
font-size: #{'min(50vh, 40vw)'};
|
||||||
|
opacity: 0.025;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact {
|
||||||
|
text-align: center;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 24px;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
a {
|
||||||
|
position: relative;
|
||||||
|
padding: 16px;
|
||||||
|
|
||||||
|
color: var(--on-background);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #fff;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
opacity: 0.2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
|
||||||
|
background-image: url(../../img/logo.svg);
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center;
|
||||||
|
background-size: 64px;
|
||||||
|
|
||||||
|
opacity: 0.075;
|
||||||
|
filter: contrast(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -96,7 +96,7 @@
|
|||||||
background-color: var(--input);
|
background-color: var(--input);
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
|
|
||||||
> :global(.feather.icon) {
|
> :global(.icon) {
|
||||||
--icon-size: 24px;
|
--icon-size: 24px;
|
||||||
margin: 18px;
|
margin: 18px;
|
||||||
opacity: 0.75;
|
opacity: 0.75;
|
||||||
@ -180,7 +180,7 @@
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
& + :global(.feather) {
|
& + :global(.icon) {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
right: 0;
|
right: 0;
|
||||||
@ -190,7 +190,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Temporary
|
// TODO: Temporary
|
||||||
&:focus + :global(.feather) {
|
&:focus + :global(.icon) {
|
||||||
transform: rotateX(180deg);
|
transform: rotateX(180deg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -317,7 +317,7 @@
|
|||||||
.name {
|
.name {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
|
|
||||||
:global(.feather) {
|
:global(.icon) {
|
||||||
--icon-size: 24px;
|
--icon-size: 24px;
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,17 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {replaceIcons} from "../../ts/featherIcons.js";
|
import {replaceIcons} from "../../ts/icons.js";
|
||||||
import {afterUpdate, onMount} from "svelte";
|
import {onMount} from "svelte";
|
||||||
|
|
||||||
export let name: string;
|
export let name: string;
|
||||||
|
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
replaceIcons();
|
replaceIcons(true);
|
||||||
});
|
|
||||||
afterUpdate(() => {
|
|
||||||
replaceIcons(false);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
// ---
|
:global(.icon) {
|
||||||
// --- Feather
|
|
||||||
// ---
|
|
||||||
:global(.feather) {
|
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -43,8 +37,8 @@
|
|||||||
|
|
||||||
{#if name}
|
{#if name}
|
||||||
{#if name.startsWith('fa') }
|
{#if name.startsWith('fa') }
|
||||||
<i class="{name} feather icon" aria-hidden="true" {...$$restProps}></i>
|
<i class="{name} icon" aria-hidden="true" {...$$restProps}></i>
|
||||||
{:else}
|
{:else}
|
||||||
<i data-feather="{name}" class="feather icon" aria-hidden="true" {...$$restProps}></i>
|
<i icon-name="{name}" class="icon" aria-hidden="true" {...$$restProps}></i>
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -64,7 +64,7 @@ export default class SvelteViewEngine extends ViewEngine {
|
|||||||
const view = await this.fileCache.get(actualFile, !config.get<boolean>('view.cache'));
|
const view = await this.fileCache.get(actualFile, !config.get<boolean>('view.cache'));
|
||||||
|
|
||||||
// Root template
|
// Root template
|
||||||
const templateFile = await this.resolveFileFromCanonicalNameOrFail('layouts/svelte_layout.html');
|
const templateFile = await this.resolveFileFromCanonicalNameOrFail('templates/svelte_template.html');
|
||||||
const rawOutput = await this.fileCache.get(templateFile, !config.get<boolean>('view.cache'));
|
const rawOutput = await this.fileCache.get(templateFile, !config.get<boolean>('view.cache'));
|
||||||
|
|
||||||
// Pre-compiled parts
|
// Pre-compiled parts
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<script>
|
<script>
|
||||||
import {route} from "../../common/Routing";
|
import {route} from "../../common/Routing";
|
||||||
import BaseLayout from "./layouts/BaseLayout.svelte";
|
import BaseTemplate from "./templates/BaseTemplate.svelte";
|
||||||
import Message from "./components/Message.svelte";
|
import Message from "./components/Message.svelte";
|
||||||
import Form from "./utils/Form.svelte";
|
import Form from "./utils/Form.svelte";
|
||||||
import Field from "./utils/Field.svelte";
|
import Field from "./utils/Field.svelte";
|
||||||
@ -9,11 +9,12 @@
|
|||||||
import Breadcrumb from "./components/Breadcrumb.svelte";
|
import Breadcrumb from "./components/Breadcrumb.svelte";
|
||||||
import Icon from "./utils/Icon.svelte";
|
import Icon from "./utils/Icon.svelte";
|
||||||
import DesignButtons from "./DesignButtons.svelte";
|
import DesignButtons from "./DesignButtons.svelte";
|
||||||
|
import CopyableText from "./components/CopyableText.svelte";
|
||||||
|
|
||||||
const paginationData = new PaginationData(20, 20, 1000).serialize();
|
const paginationData = new PaginationData(20, 20, 1000).serialize();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<BaseLayout title="Design test">
|
<BaseTemplate title="Design test">
|
||||||
<DesignButtons/>
|
<DesignButtons/>
|
||||||
|
|
||||||
<div class="panel">
|
<div class="panel">
|
||||||
@ -23,7 +24,7 @@
|
|||||||
</h2>
|
</h2>
|
||||||
<p>Paragraph</p>
|
<p>Paragraph</p>
|
||||||
|
|
||||||
<p>Paragraph, <a href={route('home')}>Link</a></p>
|
<p>Paragraph, <a href={route('home')}>Link</a>, <a href={route('home')} target="_blank">External link</a></p>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>Unordered</li>
|
<li>Unordered</li>
|
||||||
@ -114,6 +115,15 @@
|
|||||||
{link: route('design'), title: 'self'},
|
{link: route('design'), title: 'self'},
|
||||||
]}/>
|
]}/>
|
||||||
</div>
|
</div>
|
||||||
</BaseLayout>
|
|
||||||
|
<div class="panel">
|
||||||
|
<h2>
|
||||||
|
<Icon name="copy"/>
|
||||||
|
Copyable text
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<CopyableText title="URL" content="https://swaf.dev"/>
|
||||||
|
</div>
|
||||||
|
</BaseTemplate>
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import AllTests from "./AllTests.svelte";
|
import AllTests from "./AllTests.svelte";
|
||||||
import BaseLayout from "./layouts/BaseLayout.svelte";
|
import BaseLayout from "./templates/BaseTemplate.svelte";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<BaseLayout title="Frontend tests">
|
<BaseLayout title="Frontend tests">
|
||||||
|
28
yarn.lock
28
yarn.lock
@ -803,11 +803,6 @@
|
|||||||
"@types/qs" "*"
|
"@types/qs" "*"
|
||||||
"@types/serve-static" "*"
|
"@types/serve-static" "*"
|
||||||
|
|
||||||
"@types/feather-icons@^4.7.0":
|
|
||||||
version "4.7.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/feather-icons/-/feather-icons-4.7.0.tgz#ec66bc046bcd1513835f87541ecef54b50c57ec9"
|
|
||||||
integrity sha512-vflOrmlHMGIGVN4AEl6ErPCNKBLcP1ehEpLqnJkTgf69r5QmJzy7BF1WzbBc8Hqs9KffROPT/JqlSKH4o5vB/w==
|
|
||||||
|
|
||||||
"@types/formidable@^2.0.0":
|
"@types/formidable@^2.0.0":
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/@types/formidable/-/formidable-2.0.0.tgz#7100d6121871c6300e16c89cf4002f3b66157359"
|
resolved "https://registry.yarnpkg.com/@types/formidable/-/formidable-2.0.0.tgz#7100d6121871c6300e16c89cf4002f3b66157359"
|
||||||
@ -1843,11 +1838,6 @@ cjs-module-lexer@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40"
|
resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40"
|
||||||
integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==
|
integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==
|
||||||
|
|
||||||
classnames@^2.2.5:
|
|
||||||
version "2.3.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e"
|
|
||||||
integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==
|
|
||||||
|
|
||||||
clean-css@^4.2.1:
|
clean-css@^4.2.1:
|
||||||
version "4.2.4"
|
version "4.2.4"
|
||||||
resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.4.tgz#733bf46eba4e607c6891ea57c24a989356831178"
|
resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.4.tgz#733bf46eba4e607c6891ea57c24a989356831178"
|
||||||
@ -2112,11 +2102,6 @@ cookiejar@^2.1.2:
|
|||||||
resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.3.tgz#fc7a6216e408e74414b90230050842dacda75acc"
|
resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.3.tgz#fc7a6216e408e74414b90230050842dacda75acc"
|
||||||
integrity sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==
|
integrity sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==
|
||||||
|
|
||||||
core-js@^3.1.3:
|
|
||||||
version "3.19.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.19.1.tgz#f6f173cae23e73a7d88fa23b6e9da329276c6641"
|
|
||||||
integrity sha512-Tnc7E9iKd/b/ff7GFbhwPVzJzPztGrChB8X8GLqoYGdEOG8IpLnK1xPyo3ZoO3HsK6TodJS58VGPOxA+hLHQMg==
|
|
||||||
|
|
||||||
core-util-is@~1.0.0:
|
core-util-is@~1.0.0:
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
|
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
|
||||||
@ -3225,14 +3210,6 @@ fd-slicer@~1.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
pend "~1.2.0"
|
pend "~1.2.0"
|
||||||
|
|
||||||
feather-icons@^4.28.0:
|
|
||||||
version "4.28.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/feather-icons/-/feather-icons-4.28.0.tgz#e1892a401fe12c4559291770ff6e68b0168e760f"
|
|
||||||
integrity sha512-gRdqKESXRBUZn6Nl0VBq2wPHKRJgZz7yblrrc2lYsS6odkNFDnA4bqvrlEVRUPjE1tFax+0TdbJKZ31ziJuzjg==
|
|
||||||
dependencies:
|
|
||||||
classnames "^2.2.5"
|
|
||||||
core-js "^3.1.3"
|
|
||||||
|
|
||||||
fetch-blob@^3.1.2:
|
fetch-blob@^3.1.2:
|
||||||
version "3.1.3"
|
version "3.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.1.3.tgz#a7dca4855e39d3e3c5a1da62d4ee335c37d26012"
|
resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.1.3.tgz#a7dca4855e39d3e3c5a1da62d4ee335c37d26012"
|
||||||
@ -5105,6 +5082,11 @@ lru-cache@^6.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
yallist "^4.0.0"
|
yallist "^4.0.0"
|
||||||
|
|
||||||
|
lucide@^0.16.17:
|
||||||
|
version "0.16.17"
|
||||||
|
resolved "https://registry.yarnpkg.com/lucide/-/lucide-0.16.17.tgz#52466954e97ea3efac0191b2452f42932d41fdf7"
|
||||||
|
integrity sha512-zAB+aRo6/naheCMQ8AYNAXCnsxCZQb7pI52taqjcfBDPkH3jJnx+b5UnvR4kncVPw4t7/U4GARH/23PleFgnHQ==
|
||||||
|
|
||||||
magic-string@^0.25.7:
|
magic-string@^0.25.7:
|
||||||
version "0.25.7"
|
version "0.25.7"
|
||||||
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051"
|
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051"
|
||||||
|
Loading…
Reference in New Issue
Block a user