Update
This commit is contained in:
34
Server/app/layouts/main.jl.html
Normal file
34
Server/app/layouts/main.jl.html
Normal file
@@ -0,0 +1,34 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>$(vars(:title))</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta property="og:title" content="$(vars(:title))">
|
||||
<meta property="og:description" content="$(vars(:description))">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:image" content="">
|
||||
<meta property="og:url" content="">
|
||||
|
||||
<!--Load libraries-->
|
||||
|
||||
<!--Load fonts-->
|
||||
|
||||
<!--Load CSS-->
|
||||
|
||||
<!--Load components-->
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<!--<loadscreen-component></loadscreen-component>-->
|
||||
|
||||
<div id="content">
|
||||
<navbar-component></navbar-component>
|
||||
|
||||
<%@yield%>
|
||||
|
||||
<footer-component></footer-component>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
0
Server/app/resources/.gitkeep
Normal file
0
Server/app/resources/.gitkeep
Normal file
24
Server/app/resources/basic/BasicController.jl
Normal file
24
Server/app/resources/basic/BasicController.jl
Normal file
@@ -0,0 +1,24 @@
|
||||
module BasicController
|
||||
|
||||
using Genie, Genie.Renderer, Genie.Renderer.Html, Genie.Requests
|
||||
using JSON3
|
||||
using SearchLight
|
||||
using Server.DatabaseSupport, Server.TemplateEditor
|
||||
|
||||
controller = "basic"
|
||||
dict_layouts = Dict(
|
||||
:landing => generate_layout_html("main",controller,"landing",css=["landing"]),
|
||||
)
|
||||
|
||||
#---General-----------------------------------------------------
|
||||
|
||||
|
||||
function landing()
|
||||
html(:basic,:landing, layout = dict_layouts[:landing], context = @__MODULE__,
|
||||
title = "LibSoc",
|
||||
description = ""
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
end
|
1
Server/app/resources/basic/views/landing.jl.html
Normal file
1
Server/app/resources/basic/views/landing.jl.html
Normal file
@@ -0,0 +1 @@
|
||||
<landing-component></landing-component>
|
4
Server/app/svelte/.gitignore
vendored
Normal file
4
Server/app/svelte/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
/node_modules/
|
||||
/public/build/
|
||||
|
||||
.DS_Store
|
107
Server/app/svelte/README.md
Normal file
107
Server/app/svelte/README.md
Normal file
@@ -0,0 +1,107 @@
|
||||
# This repo is no longer maintained. Consider using `npm init vite` and selecting the `svelte` option or — if you want a full-fledged app framework and don't mind using pre-1.0 software — use [SvelteKit](https://kit.svelte.dev), the official application framework for Svelte.
|
||||
|
||||
---
|
||||
|
||||
# svelte app
|
||||
|
||||
This is a project template for [Svelte](https://svelte.dev) apps. It lives at https://github.com/sveltejs/template.
|
||||
|
||||
To create a new project based on this template using [degit](https://github.com/Rich-Harris/degit):
|
||||
|
||||
```bash
|
||||
npx degit sveltejs/template svelte-app
|
||||
cd svelte-app
|
||||
```
|
||||
|
||||
*Note that you will need to have [Node.js](https://nodejs.org) installed.*
|
||||
|
||||
|
||||
## Get started
|
||||
|
||||
Install the dependencies...
|
||||
|
||||
```bash
|
||||
cd svelte-app
|
||||
npm install
|
||||
```
|
||||
|
||||
...then start [Rollup](https://rollupjs.org):
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Navigate to [localhost:8080](http://localhost:8080). You should see your app running. Edit a component file in `src`, save it, and reload the page to see your changes.
|
||||
|
||||
By default, the server will only respond to requests from localhost. To allow connections from other computers, edit the `sirv` commands in package.json to include the option `--host 0.0.0.0`.
|
||||
|
||||
If you're using [Visual Studio Code](https://code.visualstudio.com/) we recommend installing the official extension [Svelte for VS Code](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode). If you are using other editors you may need to install a plugin in order to get syntax highlighting and intellisense.
|
||||
|
||||
## Building and running in production mode
|
||||
|
||||
To create an optimised version of the app:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
You can run the newly built app with `npm run start`. This uses [sirv](https://github.com/lukeed/sirv), which is included in your package.json's `dependencies` so that the app will work when you deploy to platforms like [Heroku](https://heroku.com).
|
||||
|
||||
|
||||
## Single-page app mode
|
||||
|
||||
By default, sirv will only respond to requests that match files in `public`. This is to maximise compatibility with static fileservers, allowing you to deploy your app anywhere.
|
||||
|
||||
If you're building a single-page app (SPA) with multiple routes, sirv needs to be able to respond to requests for *any* path. You can make it so by editing the `"start"` command in package.json:
|
||||
|
||||
```js
|
||||
"start": "sirv public --single"
|
||||
```
|
||||
|
||||
## Using TypeScript
|
||||
|
||||
This template comes with a script to set up a TypeScript development environment, you can run it immediately after cloning the template with:
|
||||
|
||||
```bash
|
||||
node scripts/setupTypeScript.js
|
||||
```
|
||||
|
||||
Or remove the script via:
|
||||
|
||||
```bash
|
||||
rm scripts/setupTypeScript.js
|
||||
```
|
||||
|
||||
If you want to use `baseUrl` or `path` aliases within your `tsconfig`, you need to set up `@rollup/plugin-alias` to tell Rollup to resolve the aliases. For more info, see [this StackOverflow question](https://stackoverflow.com/questions/63427935/setup-tsconfig-path-in-svelte).
|
||||
|
||||
## Deploying to the web
|
||||
|
||||
### With [Vercel](https://vercel.com)
|
||||
|
||||
Install `vercel` if you haven't already:
|
||||
|
||||
```bash
|
||||
npm install -g vercel
|
||||
```
|
||||
|
||||
Then, from within your project folder:
|
||||
|
||||
```bash
|
||||
cd public
|
||||
vercel deploy --name my-project
|
||||
```
|
||||
|
||||
### With [surge](https://surge.sh/)
|
||||
|
||||
Install `surge` if you haven't already:
|
||||
|
||||
```bash
|
||||
npm install -g surge
|
||||
```
|
||||
|
||||
Then, from within your project folder:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
surge public my-project.surge.sh
|
||||
```
|
2674
Server/app/svelte/package-lock.json
generated
Normal file
2674
Server/app/svelte/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
27
Server/app/svelte/package.json
Normal file
27
Server/app/svelte/package.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "svelte-app",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "rollup -c",
|
||||
"dev": "rollup -c -w",
|
||||
"start": "sirv public --no-clear"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-commonjs": "^17.0.0",
|
||||
"@rollup/plugin-node-resolve": "^11.0.0",
|
||||
"rollup": "^2.3.4",
|
||||
"rollup-plugin-cleaner": "^1.0.0",
|
||||
"rollup-plugin-copy": "^3.4.0",
|
||||
"rollup-plugin-css-only": "^3.1.0",
|
||||
"rollup-plugin-livereload": "^2.0.0",
|
||||
"rollup-plugin-svelte": "^7.0.0",
|
||||
"rollup-plugin-terser": "^7.0.0",
|
||||
"rollup-plugin-watch": "^1.0.2",
|
||||
"svelte": "^3.0.0",
|
||||
"walk-sync": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"sirv-cli": "^2.0.0"
|
||||
}
|
||||
}
|
532
Server/app/svelte/public/css/common.css
Normal file
532
Server/app/svelte/public/css/common.css
Normal file
@@ -0,0 +1,532 @@
|
||||
|
||||
:root {
|
||||
--light-blue:hsl(195, 67%, 95%);
|
||||
--darker-pink:hsl(344, 60%, 47%);
|
||||
--pink:hsl(344, 73%, 57%);
|
||||
--dark-green:hsl(176, 63%, 25%);
|
||||
--green:hsl(147, 33%, 60%);
|
||||
--orange:hsl(30, 97%, 72%);
|
||||
--light-orange: hsl(19, 76%, 72%);
|
||||
--dark-brown:hsl(23, 47%, 20%);
|
||||
--brown:hsl(23, 47%, 30%);
|
||||
--light-brown: hsl(23, 47%, 50%);
|
||||
--dark-pink:hsl(343, 39%, 16%);
|
||||
--red:hsl(359, 72%, 61%);
|
||||
--dark-blue:hsl(217, 25%, 16%);
|
||||
--grey-blue:hsl(223, 13%, 22%);
|
||||
--cream:hsl(34, 43%, 90%);
|
||||
--dark-cream:hsl(33, 26%, 84%);
|
||||
--sans-serif: "Space Grotesk";
|
||||
--serif: "DejaVu";
|
||||
}
|
||||
|
||||
svg {
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
:not(svg *) {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
border: none;
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
html {
|
||||
overflow-x: hidden;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
body {
|
||||
position: relative;
|
||||
min-height: 100vh;
|
||||
max-width: 100vw;
|
||||
overflow: hidden;
|
||||
background: white;
|
||||
}
|
||||
|
||||
#content {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
height: 100%;
|
||||
min-height: 100vh;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
/*---Fonts---------------------------------------------------------*/
|
||||
|
||||
@font-face {
|
||||
font-family: 'Space Grotesk';
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
src: /*local('Space Grotesk Light'),
|
||||
local('SpaceGroteskLight-Regular'),
|
||||
local('Space Grotesk Light Regular'), */
|
||||
url('/fonts/SpaceGrotesk/SpaceGrotesk.woff2') format('woff2'),
|
||||
url('/fonts/SpaceGrotesk/SpaceGrotesk.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'DejaVu';
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
src: /*local('DejaVuLGCSerif'),
|
||||
local('DejaVu LGC Serif'), */
|
||||
url('/fonts/DejaVu/DejaVuLGCSerif.woff2') format('woff2'),
|
||||
url('/fonts/DejaVu/DejaVuLGCSerif.woff') format('woff');
|
||||
}
|
||||
|
||||
.serif {
|
||||
font-family: var(--serif)
|
||||
}
|
||||
|
||||
.sans-serif {
|
||||
font-family: var(--sans-serif)
|
||||
}
|
||||
|
||||
|
||||
/*---Text related------------------------------------------------------------------------------*/
|
||||
|
||||
b {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-family: var(--sans-serif, sans-serif);
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 2rem;
|
||||
font-family: var(--sans-serif, sans-serif);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-family: var(--sans-serif, sans-serif);
|
||||
font-size: 1.6rem
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-family: var(--sans-serif, sans-serif);
|
||||
font-size: 1.4rem
|
||||
}
|
||||
|
||||
span {
|
||||
font-family: var(--serif, serif);
|
||||
font-size: 1.3rem;
|
||||
height: auto;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
p {
|
||||
font-family: var(--serif, serif);
|
||||
font-size: 1.2rem;
|
||||
line-height: 170%;
|
||||
}
|
||||
|
||||
b,i,s {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
label {
|
||||
font-family: var(--serif, serif);
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
sup {
|
||||
vertical-align: top;
|
||||
position: relative;
|
||||
top: -0.682rem;
|
||||
}
|
||||
|
||||
sub {
|
||||
vertical-align: top;
|
||||
position: relative;
|
||||
top: 0.34rem;
|
||||
}
|
||||
|
||||
|
||||
a {
|
||||
font-family: var(--serif, serif);
|
||||
font-size: 1.638rem;
|
||||
color: black;
|
||||
}
|
||||
|
||||
a:link { text-decoration: none; }
|
||||
|
||||
a:visited { text-decoration: none; }
|
||||
|
||||
a:hover { text-decoration: none; }
|
||||
|
||||
a:active { text-decoration: none; }
|
||||
|
||||
a:focus { text-decoration: none; }
|
||||
|
||||
.justify {
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
.center {
|
||||
width: 100%;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.margin-end {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.margin-end2 {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.text p, .text li {
|
||||
font-size: 1.2rem;
|
||||
font-family: var(--serif);
|
||||
line-height: 170%;
|
||||
}
|
||||
|
||||
.text li {
|
||||
margin-left: 1.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
line-height: 140%;
|
||||
}
|
||||
|
||||
.text ul p {
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.text h2 {
|
||||
width: max-content;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
margin: auto;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.title-highlight {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.title-highlight::before {
|
||||
position: absolute;
|
||||
margin-left: -0.68rem;
|
||||
margin-top: -0.14rem;
|
||||
width: 2.5rem;
|
||||
height: 0rem;
|
||||
content: "";
|
||||
border-left: 0.19rem solid var(--pink);
|
||||
border-top: 0.19rem solid var(--pink);
|
||||
border-radius: 1rem;
|
||||
}
|
||||
|
||||
.title-highlight::after {
|
||||
position: absolute;
|
||||
left:0;
|
||||
top: 0;
|
||||
margin-left: -0.68rem;
|
||||
margin-top: -0.14rem;
|
||||
width: 0rem;
|
||||
height: 1.7rem;
|
||||
content: "";
|
||||
border-left: 0.19rem solid var(--pink);
|
||||
border-top: 0.19rem solid var(--pink);
|
||||
border-radius: 1rem;
|
||||
}
|
||||
|
||||
.text h3 {
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.text h4 {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.text p {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.text ul>ul>li {
|
||||
margin-left: 2.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
line-height: 140%;
|
||||
}
|
||||
|
||||
/*---Button/input related--------------------------------------------------------------*/
|
||||
|
||||
button {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
button:focus {
|
||||
outline:0;
|
||||
}
|
||||
|
||||
|
||||
input[type="text"],input[type="email"],input[type="password"],input[type="number"] {
|
||||
font-family: var(--serif, serif);
|
||||
background-color: white;
|
||||
border-radius: 0.4rem;
|
||||
border-color: black;
|
||||
border: black solid 0.063rem;
|
||||
padding-left: 0.5rem;
|
||||
font: 1.3rem var(--serif, serif);
|
||||
}
|
||||
|
||||
input[type="text"] {
|
||||
-webkit-appearance: textfield;
|
||||
-moz-appearance: textfield;
|
||||
appearance: textfield;
|
||||
color: #353535;
|
||||
}
|
||||
|
||||
input[type="email"] {
|
||||
-webkit-appearance: textfield;
|
||||
-moz-appearance: textfield;
|
||||
appearance: textfield;
|
||||
font-weight: normal;
|
||||
color: #353535;
|
||||
height: 2.7rem;
|
||||
padding-left: 0.34rem;
|
||||
}
|
||||
|
||||
input[type="password"] {
|
||||
padding-left: 0.34rem;
|
||||
-webkit-appearance: textfield;
|
||||
-moz-appearance: textfield;
|
||||
appearance: textfield;
|
||||
font-weight: normal;
|
||||
color: #353535;
|
||||
}
|
||||
|
||||
input[type="number"] {
|
||||
-webkit-appearance: textfield;
|
||||
-moz-appearance: textfield;
|
||||
appearance: textfield;
|
||||
color: #353535;
|
||||
}
|
||||
|
||||
input[type=number]::-webkit-inner-spin-button,
|
||||
input[type=number]::-webkit-outer-spin-button {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
/*---Scroll button---------------------------------------------------------------------*/
|
||||
|
||||
.scroll-button {
|
||||
cursor: pointer;
|
||||
background-color: #EBF7FB;
|
||||
border-color: #EBF7FB;
|
||||
height: 3.822rem;
|
||||
width: 3.822rem;
|
||||
border-radius: 3.822rem;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
padding-top: 0.505rem;
|
||||
}
|
||||
|
||||
.scroll-button img {
|
||||
width: 1.9rem;
|
||||
padding-bottom: 0.4rem;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.next {
|
||||
padding-left: 0.382rem;
|
||||
}
|
||||
|
||||
.prev {
|
||||
padding-right: 0.382rem;
|
||||
}
|
||||
|
||||
|
||||
/*---Grid related----------------------------------------------------------------------*/
|
||||
|
||||
.column {
|
||||
display: grid;
|
||||
grid-auto-flow: column
|
||||
}
|
||||
|
||||
.center-items {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
|
||||
/*---Fillers-------------------------------------------------------------------------*/
|
||||
|
||||
.filler1 {
|
||||
width: 100%;
|
||||
height: 0.655rem;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.filler2 {
|
||||
width: 100%;
|
||||
height: 1.263rem;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.filler3 {
|
||||
width: 100%;
|
||||
height: 2.525rem;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.filler4 {
|
||||
width: 100%;
|
||||
height: 3.822rem;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.filler5 {
|
||||
width: 100%;
|
||||
height: 5.05rem;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.filler6 {
|
||||
width: 100%;
|
||||
height: 6.347rem;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
|
||||
/*---Margins------------------------------------------------------------------------*/
|
||||
|
||||
.bmargin1 {
|
||||
margin-bottom: 0.5rem
|
||||
}
|
||||
|
||||
.bmargin2 {
|
||||
margin-bottom: 1rem
|
||||
}
|
||||
|
||||
.bmargin3 {
|
||||
margin-bottom: 1.5rem
|
||||
}
|
||||
|
||||
.tmargin1 {
|
||||
margin-top: 0.5rem
|
||||
}
|
||||
|
||||
.tmargin2 {
|
||||
margin-top: 1rem
|
||||
}
|
||||
|
||||
.tmargin3 {
|
||||
margin-top: 1.5rem
|
||||
}
|
||||
|
||||
|
||||
/*---Other-------------------------------------------------------------------------*/
|
||||
|
||||
.flex-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.flex-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.pane {
|
||||
background: white;
|
||||
border: 0;
|
||||
border-radius: 0.635rem;
|
||||
box-shadow: 0 0 0.314rem rgb(187, 187, 187);
|
||||
}
|
||||
|
||||
.pane-container {
|
||||
margin-top: min(3vw, 1rem);
|
||||
margin-bottom: 3.1rem;
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
column-gap: 5rem;
|
||||
}
|
||||
|
||||
.pane-centering {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.main-pane {
|
||||
position: relative;
|
||||
margin: auto;
|
||||
height: min-content;
|
||||
max-width: 66rem;
|
||||
width: 66rem;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.clicked {
|
||||
background-color: hsla(344, 73%, 57%, 0.25);
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1117px) {
|
||||
.main-pane {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*---Scaling-----------------------------------------------------------------------*/
|
||||
|
||||
html {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 660px) {
|
||||
html {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 570px) {
|
||||
html {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 470px) {
|
||||
html {
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 400px) {
|
||||
html {
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 360px) {
|
||||
html {
|
||||
font-size: 9px;
|
||||
}
|
||||
}
|
177
Server/app/svelte/public/css/footer.css
Normal file
177
Server/app/svelte/public/css/footer.css
Normal file
@@ -0,0 +1,177 @@
|
||||
/*---Footer----------------------------------------------------*/
|
||||
|
||||
footer {
|
||||
position: relative;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
background: var(--dark-green);
|
||||
}
|
||||
|
||||
footer p, footer a {
|
||||
font-family: var(--sans-serif);
|
||||
}
|
||||
|
||||
#footer-content-container {
|
||||
position: relative;
|
||||
margin: auto;
|
||||
padding-top: 2rem;
|
||||
max-width: 116rem;
|
||||
width: 97vw;
|
||||
}
|
||||
|
||||
#footer-grid-content-container {
|
||||
display: grid;
|
||||
margin-left: 2rem;
|
||||
margin-right: 2rem;
|
||||
margin-bottom: 1.4rem;
|
||||
}
|
||||
|
||||
.not-logged {
|
||||
grid-template-columns: auto auto auto 2rem;
|
||||
}
|
||||
|
||||
.logged {
|
||||
grid-template-columns: auto auto 2rem;
|
||||
}
|
||||
|
||||
footer h2 {
|
||||
color: #ffffff;
|
||||
font-size: 1.4rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
#footer-copyright {
|
||||
position: relative;
|
||||
color: #ffffff;
|
||||
margin-left: 2rem;
|
||||
bottom: 0rem;
|
||||
font-weight: 400;
|
||||
font-size: 1.2rem;
|
||||
height: 3rem;
|
||||
top: 0rem;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
footer a {
|
||||
font-size: 1.2rem;
|
||||
color: #d8d8d8;
|
||||
}
|
||||
|
||||
footer p, footer label {
|
||||
display: block;
|
||||
font-size: 1.2rem;
|
||||
color: #d8d8d8;
|
||||
font-family: var(--sans-serif,sans-serif);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
#newsletter-container h3 {
|
||||
margin-bottom: 0.99rem;
|
||||
}
|
||||
|
||||
#newsletter-container p {
|
||||
width: 28.665rem;
|
||||
margin-bottom: 0.48rem;
|
||||
}
|
||||
|
||||
#newsletterEmailInput {
|
||||
height: 2.5rem;
|
||||
width: 20.5rem;
|
||||
border: 0;
|
||||
border-radius: 0.4rem 0 0 0.4rem;
|
||||
filter: drop-shadow( 0.07rem 0.14rem 0.07rem rgb(0 0 0 / 0.4));
|
||||
}
|
||||
|
||||
#newsletterEmailInput::placeholder { /* Chrome, Firefox, Opera, Safari 10.1+ */
|
||||
color: var(--c,gray);
|
||||
opacity: 1; /* Firefox */
|
||||
}
|
||||
|
||||
#newsletterEmailButton {
|
||||
position: absolute;
|
||||
width: 6.8rem;
|
||||
height: 2.5rem;
|
||||
background: var(--pink);
|
||||
color: #ffffff;
|
||||
font-family: var(--sans-serif,sans-serif);
|
||||
font-size: 1.4rem;
|
||||
border-radius: 0 0.2rem 0.2rem 0;
|
||||
filter: drop-shadow( 0.07rem 0.14rem 0.07rem rgb(0 0 0 / 0.4));
|
||||
}
|
||||
|
||||
#newsletterEmailButton:active {
|
||||
background: var(--darker-pink);
|
||||
}
|
||||
|
||||
#contact-us-container {
|
||||
width: 14rem;
|
||||
}
|
||||
|
||||
#contact-us-container h3 {
|
||||
margin-bottom: 0.99rem;
|
||||
}
|
||||
|
||||
#legal-info-container {
|
||||
width: 9.6rem;
|
||||
}
|
||||
|
||||
#legal-info-container h3 {
|
||||
margin-bottom: 0.99rem;
|
||||
}
|
||||
|
||||
#legal-info-container p {
|
||||
margin-bottom: 0.48rem;
|
||||
}
|
||||
|
||||
#legal-info-container a {
|
||||
display: block;
|
||||
margin-bottom: 0.48rem;
|
||||
}
|
||||
|
||||
|
||||
#footer-up {
|
||||
position: absolute;
|
||||
width: 4.8rem;
|
||||
height: 4.8rem;
|
||||
border-radius: 3.4rem;
|
||||
top: 4rem;
|
||||
right: 2rem;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
#footer-up svg {
|
||||
width: 40%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.grass{
|
||||
position: absolute;
|
||||
top: -3.8rem;
|
||||
--height: 3.82rem;
|
||||
filter: drop-shadow( 0.07rem 0.14rem 0.07rem rgb(0 0 0 / 0.4));
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1170px) {
|
||||
.not-logged {
|
||||
grid-template-rows: auto auto auto auto;
|
||||
grid-template-columns: auto;
|
||||
row-gap: 2rem;
|
||||
}
|
||||
|
||||
.logged {
|
||||
grid-template-rows: auto auto auto;
|
||||
grid-template-columns: auto;
|
||||
row-gap: 2rem;
|
||||
}
|
||||
|
||||
#footer-copyright {
|
||||
height: 1rem;
|
||||
top: -2rem;
|
||||
}
|
||||
|
||||
#newsletterEmailInput {
|
||||
width: 18rem;
|
||||
}
|
||||
}
|
0
Server/app/svelte/public/css/landing.css
Normal file
0
Server/app/svelte/public/css/landing.css
Normal file
200
Server/app/svelte/public/css/navbar.css
Normal file
200
Server/app/svelte/public/css/navbar.css
Normal file
@@ -0,0 +1,200 @@
|
||||
|
||||
/* Header */
|
||||
#navbar{
|
||||
position: relative;
|
||||
top: 0;
|
||||
width: min(100%,116rem);
|
||||
z-index: 1000;
|
||||
height: 5.26rem;
|
||||
}
|
||||
|
||||
#navbar * {
|
||||
font-family: var(--sans-serif, sans-serif);
|
||||
}
|
||||
|
||||
/* Logo */
|
||||
#logo-container {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
margin-left: 1rem;
|
||||
height: 100%;
|
||||
max-height: 5.26rem;
|
||||
color: black;
|
||||
z-index: 1;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#navbar-logo {
|
||||
width: 3.16rem;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
#navbar-logo-text {
|
||||
position: relative;
|
||||
width: auto;
|
||||
height: 100%;
|
||||
line-height: 400%;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
font-size: 1.4rem;
|
||||
color: #292222;
|
||||
font-family: var(--sans-serif, sans-serif);
|
||||
font-weight: 400;
|
||||
padding-left: 1.2rem;
|
||||
}
|
||||
|
||||
/* Nav menu */
|
||||
#nav {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: white;
|
||||
overflow: hidden;
|
||||
z-index: 0;
|
||||
}
|
||||
#menu a{
|
||||
display: block;
|
||||
padding: 1.9rem;
|
||||
color: black;
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
#menu a:hover {
|
||||
background-color: rgb(220, 220, 220);
|
||||
}
|
||||
|
||||
#menu a:active{
|
||||
background-color: #f7aec0;
|
||||
}
|
||||
|
||||
#nav{
|
||||
max-height: 0;
|
||||
/*transition: max-height .5s ease-out;*/
|
||||
}
|
||||
|
||||
/* Menu Icon */
|
||||
#hamb{
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
right: 0rem;
|
||||
padding: 2.8rem 2rem;
|
||||
z-index: 9999;
|
||||
}/* Style label tag */
|
||||
|
||||
#hamb-line {
|
||||
background: black;
|
||||
display: block;
|
||||
height: 2px;
|
||||
position: relative;
|
||||
width: 24px;
|
||||
|
||||
} /* Style span tag */
|
||||
|
||||
#hamb-line::before,
|
||||
#hamb-line::after{
|
||||
background: black;
|
||||
content: '';
|
||||
display: block;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
transition: all .2s ease-out;
|
||||
width: 100%;
|
||||
}
|
||||
#hamb-line::before{
|
||||
top: 5px;
|
||||
}
|
||||
#hamb-line::after{
|
||||
top: -5px;
|
||||
}
|
||||
|
||||
#side-menu {
|
||||
display: none;
|
||||
} /* Hide checkbox */
|
||||
|
||||
/* Toggle menu icon */
|
||||
#side-menu:checked ~ nav {
|
||||
display: block;
|
||||
max-height: 100%;
|
||||
padding-top: 5.625rem;
|
||||
}
|
||||
|
||||
#side-menu:checked ~ #logo-container {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
#side-menu:checked ~ #hamb {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
#side-menu:checked ~ #hamb #hamb-line {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
#side-menu:checked ~ #hamb #hamb-line::before {
|
||||
transform: rotate(-45deg);
|
||||
top:0;
|
||||
}
|
||||
|
||||
#side-menu:checked ~ #hamb #hamb-line::after {
|
||||
transform: rotate(45deg);
|
||||
top:0;
|
||||
}
|
||||
|
||||
#cart-icon {
|
||||
height: 1.8rem;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#menu a:hover div {
|
||||
filter: saturate(50%) brightness(140%);
|
||||
}
|
||||
|
||||
#menu a:hover svg {
|
||||
stroke: rgb(127, 127, 127);;
|
||||
}
|
||||
|
||||
#cart-counter {
|
||||
position: relative;
|
||||
top: -2.8rem;
|
||||
left: 1.6rem;
|
||||
width: 1.3rem;
|
||||
height: 1.3rem;
|
||||
border-radius: 3.4rem;
|
||||
font-family: var(--sans-serif, sans-serif);
|
||||
font-size: 1rem;
|
||||
text-align: center;
|
||||
color: white;
|
||||
background: var(--pink);
|
||||
}
|
||||
|
||||
|
||||
/* Responsiveness */
|
||||
@media only screen and (min-width: 1500px) {
|
||||
|
||||
#navbar{
|
||||
left: 50%;
|
||||
-ms-transform: translateX(-50%);
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
#nav{
|
||||
max-height: none;
|
||||
top: 0;
|
||||
position: relative;
|
||||
float: right;
|
||||
width: fit-content;
|
||||
background-color: transparent;
|
||||
}
|
||||
#menu li{
|
||||
float: left;
|
||||
}
|
||||
#menu a:hover{
|
||||
background-color: transparent;
|
||||
color: rgb(127, 127, 127);
|
||||
}
|
||||
|
||||
#hamb{
|
||||
display: none;
|
||||
}
|
||||
}
|
35
Server/app/svelte/public/js/libraries/cssTools.js
Normal file
35
Server/app/svelte/public/js/libraries/cssTools.js
Normal file
@@ -0,0 +1,35 @@
|
||||
|
||||
export default class CssTools {
|
||||
|
||||
static applyCSS(element,id,outerclass,classlisthint) {
|
||||
// Return if no CSS to apply
|
||||
if (classlisthint==null || (outerclass==null && id==null)) {
|
||||
return
|
||||
}
|
||||
|
||||
let outerClassNames = outerclass.split(" ").map(x => "."+x)
|
||||
let outerCssSelectors = [...outerClassNames,"#"+id]
|
||||
|
||||
let styleSheets = document.styleSheets
|
||||
let l = styleSheets.length
|
||||
for (let i=0; i<l; i++) {
|
||||
let sheet = styleSheets[i]
|
||||
let name = sheet.href.split("/").at(-1).split(".")[0]
|
||||
if (name===classlisthint) {
|
||||
for (let outerCss of outerCssSelectors) {
|
||||
for (let rule of sheet.cssRules) {
|
||||
if (rule.selectorText==outerCss) {
|
||||
let styleRule = rule.style
|
||||
let vals = Object.values(styleRule)
|
||||
for (let val of vals) {
|
||||
element.style[val] = styleRule[val]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
146
Server/app/svelte/public/js/libraries/mathTools.js
Normal file
146
Server/app/svelte/public/js/libraries/mathTools.js
Normal file
@@ -0,0 +1,146 @@
|
||||
|
||||
// Broadcast array on array
|
||||
export function bAr(fun,ar1,ar2) {
|
||||
var result = new Array(ar1.length)
|
||||
for (var i = 0;i<ar1.length;i++) {
|
||||
result[i] = fun(ar1[i],ar2[i])
|
||||
}
|
||||
return(result)
|
||||
}
|
||||
|
||||
// Broadcast array on a number
|
||||
export function b(fun,ar1,val) {
|
||||
var result = new Array(ar1.length)
|
||||
for (var i = 0;i<ar1.length;i++) {
|
||||
result[i] = fun(ar1[i],val)
|
||||
}
|
||||
return(result)
|
||||
}
|
||||
|
||||
// Broadcast array on a function
|
||||
export function bFun(fun,ar1) {
|
||||
var result = new Array(ar1.length)
|
||||
for (var i = 0;i<ar1.length;i++) {
|
||||
result[i] = fun(ar1[i])
|
||||
}
|
||||
return(result)
|
||||
}
|
||||
|
||||
export function multiply(num1, num2) {
|
||||
let result = num1 * num2;
|
||||
return result;
|
||||
}
|
||||
|
||||
export function sum(ar) {
|
||||
return ar.reduce((a, b) => a + b, 0)
|
||||
}
|
||||
|
||||
export function prod(ar) {
|
||||
return ar.reduce((a, b) => a * b, 1)
|
||||
}
|
||||
|
||||
export function mean(ar) {
|
||||
return ar.reduce((a, b) => a + b, 0)/ar.length
|
||||
}
|
||||
|
||||
export function median(values){
|
||||
if(values.length ===0) throw new Error("No inputs");
|
||||
|
||||
values.sort(function(a,b){
|
||||
return a-b;
|
||||
});
|
||||
|
||||
var half = Math.floor(values.length / 2);
|
||||
|
||||
if (values.length % 2)
|
||||
return values[half];
|
||||
|
||||
return (values[half - 1] + values[half]) / 2.0;
|
||||
}
|
||||
|
||||
export function range(start,end, step = start<end ? 1 : -1) {
|
||||
var arr = []
|
||||
if (step>0) {
|
||||
for (var i = start; i <= end; i += step) {
|
||||
arr.push(i)
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (var i = start; i >= end; i += step) {
|
||||
arr.push(i)
|
||||
}
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
export function numericallyIntegrate(f, a, b, dx) {
|
||||
|
||||
// calculate the number of trapezoids
|
||||
let n = (b - a) / dx
|
||||
|
||||
// define the variable for area
|
||||
let Area = 0
|
||||
|
||||
//loop to calculate the area of each trapezoid and sum.
|
||||
for (let i = 1; i <= n; i++) {
|
||||
//the x locations of the left and right side of each trapezpoid
|
||||
let x0 = a + (i-1)*dx
|
||||
let x1 = a + i*dx
|
||||
|
||||
// the area of each trapezoid
|
||||
let Ai = dx * (f(x0) + f(x1))/ 2.
|
||||
|
||||
// cumulatively sum the areas
|
||||
Area = Area + Ai
|
||||
|
||||
}
|
||||
return Area
|
||||
}
|
||||
|
||||
const randomNormals = () => {
|
||||
let u1 = 0, u2 = 0;
|
||||
//Convert [0,1) to (0,1)
|
||||
while (u1 === 0) u1 = Math.random();
|
||||
while (u2 === 0) u2 = Math.random();
|
||||
const R = Math.sqrt(-2.0 * Math.log(u1));
|
||||
const Θ = 2.0 * Math.PI * u2;
|
||||
return [R * Math.cos(Θ), R * Math.sin(Θ)];
|
||||
};
|
||||
|
||||
export function randomSkewNormal(ξ, ω, α = 0) {
|
||||
const [u0, v] = randomNormals();
|
||||
if (α === 0) {
|
||||
return ξ + ω * u0;
|
||||
}
|
||||
const 𝛿 = α / Math.sqrt(1 + α * α);
|
||||
const u1 = 𝛿 * u0 + Math.sqrt(1 - 𝛿 * 𝛿) * v;
|
||||
const z = u0 >= 0 ? u1 : -u1;
|
||||
return ξ + ω * z;
|
||||
};
|
||||
|
||||
export function percentile(arr, p) {
|
||||
arr.sort(function(a,b){
|
||||
return a-b;
|
||||
});
|
||||
if (arr.length === 0) return 0;
|
||||
if (typeof p !== 'number') throw new TypeError('p must be a number');
|
||||
if (p <= 0) return arr[0];
|
||||
if (p >= 1) return arr[arr.length - 1];
|
||||
|
||||
var index = (arr.length - 1) * p,
|
||||
lower = Math.floor(index),
|
||||
upper = lower + 1,
|
||||
weight = index % 1;
|
||||
|
||||
if (upper >= arr.length) return arr[lower];
|
||||
return arr[lower] * (1 - weight) + arr[upper] * weight;
|
||||
}
|
||||
|
||||
export function smooth(ar,window) {
|
||||
let windowHalf = window/2
|
||||
let windowHalf1 = Math.floor(windowHalf)
|
||||
let windowHalf2 = Math.ceil(windowHalf)
|
||||
for (let i=windowHalf1;i<ar.length-windowHalf2;i++) {
|
||||
ar[i] = mean(ar.slice(i-windowHalf1,i+windowHalf2))
|
||||
}
|
||||
}
|
112
Server/app/svelte/public/js/libraries/miscTools.js
Normal file
112
Server/app/svelte/public/js/libraries/miscTools.js
Normal file
@@ -0,0 +1,112 @@
|
||||
|
||||
export function isObject(v) {
|
||||
return typeof v === 'object' && !Array.isArray(v) && v !== null
|
||||
}
|
||||
|
||||
// Prevents excessive function calling
|
||||
export function debounce(func, timeout){
|
||||
let timer;
|
||||
return (...args) => {
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(() => { func.apply(this, args); }, timeout)
|
||||
}
|
||||
}
|
||||
|
||||
export function svgFromObject(object) {
|
||||
var objectDoc = object.contentDocument;
|
||||
var svgItem = objectDoc.querySelector("path");
|
||||
return svgItem
|
||||
}
|
||||
|
||||
export function rem2px(rem) {
|
||||
let fontSizeString = window.getComputedStyle(document.getElementsByTagName("html")[0]).getPropertyValue('font-size')
|
||||
let fontSize = parseFloat(fontSizeString.substring(0,fontSizeString.length-2))
|
||||
return fontSize * rem
|
||||
}
|
||||
|
||||
export function px2rem(px) {
|
||||
let fontSizeString = window.getComputedStyle(document.getElementsByTagName("html")[0]).getPropertyValue('font-size')
|
||||
let fontSize = parseFloat(fontSizeString.substring(0,fontSizeString.length-2))
|
||||
return px / fontSize
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses canvas.measureText to compute and return the width of the given text of given font in pixels.
|
||||
*
|
||||
* @param {String} text The text to be rendered.
|
||||
* @param {String} font The css font descriptor that text is to be rendered with (e.g. "bold 14px verdana").
|
||||
*
|
||||
* @see https://stackoverflow.com/questions/118241/calculate-text-width-with-javascript/21015393#21015393
|
||||
*/
|
||||
|
||||
export function getTextWidth(text, font) {
|
||||
// re-use canvas object for better performance
|
||||
const canvas = getTextWidth.canvas || (getTextWidth.canvas = document.createElement("canvas"));
|
||||
const context = canvas.getContext("2d");
|
||||
context.font = font;
|
||||
const metrics = context.measureText(text);
|
||||
return metrics.width;
|
||||
}
|
||||
|
||||
function getCssStyle(element, prop) {
|
||||
return window.getComputedStyle(element, null).getPropertyValue(prop);
|
||||
}
|
||||
|
||||
export function getCanvasFont(el = document.body) {
|
||||
const fontWeight = getCssStyle(el, 'font-weight') || 'normal';
|
||||
const fontSize = getCssStyle(el, 'font-size') || '16px';
|
||||
const fontFamily = getCssStyle(el, 'font-family') || 'Times New Roman';
|
||||
|
||||
return `${fontWeight} ${fontSize} ${fontFamily}`;
|
||||
}
|
||||
|
||||
export function validateAge(event,input,callback) {
|
||||
event.returnValue = false
|
||||
var key
|
||||
if (event.type === 'paste') {
|
||||
key = event.clipboardData.getData('text/plain');
|
||||
}
|
||||
else {
|
||||
// Handle key press
|
||||
key = event.keyCode || event.which;
|
||||
key = String.fromCharCode(key);
|
||||
}
|
||||
let keys = ["0","1","2","3","4","5","6","7","8","9"]
|
||||
if (key in keys) {
|
||||
var val = parseFloat(input.value)
|
||||
if (val>120) {
|
||||
input.value = 120
|
||||
}
|
||||
else {
|
||||
input.value = val
|
||||
}
|
||||
if (callback!=undefined) {
|
||||
callback(input.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function validatePosNumber(event,input,callback,max) {
|
||||
event.returnValue = false
|
||||
var key
|
||||
if (event.type === 'paste') {
|
||||
key = event.clipboardData.getData('text/plain');
|
||||
}
|
||||
else {
|
||||
// Handle key press
|
||||
key = event.data;
|
||||
}
|
||||
let keys = ["0","1","2","3","4","5","6","7","8","9"]
|
||||
if (key in keys) {
|
||||
var val = parseFloat(input.value)
|
||||
if (val>max) {
|
||||
input.value = max
|
||||
}
|
||||
if (callback!=undefined) {
|
||||
callback(input.value)
|
||||
}
|
||||
}
|
||||
else {
|
||||
callback(input.value)
|
||||
}
|
||||
}
|
64
Server/app/svelte/public/js/libraries/serverTools.js
Normal file
64
Server/app/svelte/public/js/libraries/serverTools.js
Normal file
@@ -0,0 +1,64 @@
|
||||
|
||||
// Get data from server
|
||||
export function getData(path,callback) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.overrideMimeType("application/json");
|
||||
xhr.open('GET', path, true);
|
||||
xhr.onreadystatechange = function () {
|
||||
if (xhr.readyState == 4 && xhr.status == "200") {
|
||||
if (callback !== undefined) {
|
||||
callback(xhr.responseText);
|
||||
}
|
||||
}
|
||||
};
|
||||
xhr.send(null);
|
||||
}
|
||||
|
||||
// Parse JSON from given path into a given variable under a given key
|
||||
export function getJSON(variable,key,path) {
|
||||
getData(path,function(response) {
|
||||
// Parse JSON string into object
|
||||
variable[key] = JSON.parse(response);
|
||||
});
|
||||
}
|
||||
|
||||
export function sendData(route,data,callback) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("POST", route, true)
|
||||
xhr.setRequestHeader('Content-Type', 'application/json')
|
||||
xhr.onreadystatechange = function() {
|
||||
if(xhr.readyState === XMLHttpRequest.DONE) {
|
||||
var status = xhr.status;
|
||||
if (status === 0 || (status >= 200 && status < 400)) {
|
||||
// The request has been completed successfully
|
||||
if (callback !== undefined) {
|
||||
callback(xhr.responseText)
|
||||
}
|
||||
} else {
|
||||
// Oh no! There has been an error with the request!
|
||||
}
|
||||
}
|
||||
};
|
||||
xhr.send(JSON.stringify(data))
|
||||
}
|
||||
|
||||
export function sendText(route,data,callback) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("POST", route, true)
|
||||
xhr.setRequestHeader('Content-Type', 'text/plain')
|
||||
xhr.onreadystatechange = function() {
|
||||
if(xhr.readyState === XMLHttpRequest.DONE) {
|
||||
var status = xhr.status;
|
||||
if (status === 0 || (status >= 200 && status < 400)) {
|
||||
// The request has been completed successfully
|
||||
if (callback !== undefined) {
|
||||
callback(xhr.responseText)
|
||||
}
|
||||
} else {
|
||||
// Oh no! There has been an error with the request!
|
||||
}
|
||||
}
|
||||
};
|
||||
xhr.send(data)
|
||||
}
|
||||
|
97
Server/app/svelte/rollup.config.js
Normal file
97
Server/app/svelte/rollup.config.js
Normal file
@@ -0,0 +1,97 @@
|
||||
import svelte from 'rollup-plugin-svelte';
|
||||
import commonjs from '@rollup/plugin-commonjs';
|
||||
import resolve from '@rollup/plugin-node-resolve';
|
||||
import livereload from 'rollup-plugin-livereload';
|
||||
import { terser } from 'rollup-plugin-terser';
|
||||
import css from 'rollup-plugin-css-only';
|
||||
import cleaner from 'rollup-plugin-cleaner';
|
||||
import copy from 'rollup-plugin-copy';
|
||||
import watch from "rollup-plugin-watch";
|
||||
|
||||
const production = !process.env.ROLLUP_WATCH;
|
||||
|
||||
|
||||
function serve() {
|
||||
let server;
|
||||
|
||||
function toExit() {
|
||||
if (server) server.kill(0);
|
||||
}
|
||||
|
||||
return {
|
||||
writeBundle() {
|
||||
if (server) return;
|
||||
server = require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
|
||||
stdio: ['ignore', 'inherit', 'inherit'],
|
||||
shell: true
|
||||
});
|
||||
|
||||
process.on('SIGTERM', toExit);
|
||||
process.on('exit', toExit);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const walkSync = require("walk-sync");
|
||||
const paths = walkSync("./src", {globs: ["**/*.svelte"]}).map(x => "src/"+x)
|
||||
|
||||
|
||||
export default {
|
||||
|
||||
input: paths,
|
||||
output: {
|
||||
format: "esm",
|
||||
dir: "../../public/js/components",
|
||||
},
|
||||
plugins: [
|
||||
svelte({
|
||||
compilerOptions: {
|
||||
// enable run-time checks when not in production
|
||||
dev: !production,
|
||||
customElement: true,
|
||||
}
|
||||
}),
|
||||
// we'll extract any component CSS out into
|
||||
// a separate file - better for performance
|
||||
css({ output: 'bundle.css' }),
|
||||
|
||||
// If you have external dependencies installed from
|
||||
// npm, you'll most likely need these plugins. In
|
||||
// some cases you'll need additional configuration -
|
||||
// consult the documentation for details:
|
||||
// https://github.com/rollup/plugins/tree/master/packages/commonjs
|
||||
resolve({
|
||||
browser: true,
|
||||
dedupe: ['svelte']
|
||||
}),
|
||||
commonjs(),
|
||||
|
||||
watch({ dir: 'public' }),
|
||||
cleaner({
|
||||
targets: [
|
||||
'../../../public/js/components'
|
||||
]
|
||||
}),
|
||||
copy({
|
||||
targets: [
|
||||
{ src: 'public/*', dest: '../../public/' },
|
||||
]
|
||||
}),
|
||||
|
||||
|
||||
// In dev mode, call `npm run start` once
|
||||
// the bundle has been generated
|
||||
!production && serve(),
|
||||
|
||||
// Watch the `public` directory and refresh the
|
||||
// browser on changes when not in production
|
||||
!production && livereload('public'),
|
||||
|
||||
// If we're building for production (npm run build
|
||||
// instead of npm run dev), minify
|
||||
production && terser()
|
||||
],
|
||||
watch: {
|
||||
clearScreen: false
|
||||
}
|
||||
};
|
121
Server/app/svelte/scripts/setupTypeScript.js
Normal file
121
Server/app/svelte/scripts/setupTypeScript.js
Normal file
@@ -0,0 +1,121 @@
|
||||
// @ts-check
|
||||
|
||||
/** This script modifies the project to support TS code in .svelte files like:
|
||||
|
||||
<script lang="ts">
|
||||
export let name: string;
|
||||
</script>
|
||||
|
||||
As well as validating the code for CI.
|
||||
*/
|
||||
|
||||
/** To work on this script:
|
||||
rm -rf test-template template && git clone sveltejs/template test-template && node scripts/setupTypeScript.js test-template
|
||||
*/
|
||||
|
||||
const fs = require("fs")
|
||||
const path = require("path")
|
||||
const { argv } = require("process")
|
||||
|
||||
const projectRoot = argv[2] || path.join(__dirname, "..")
|
||||
|
||||
// Add deps to pkg.json
|
||||
const packageJSON = JSON.parse(fs.readFileSync(path.join(projectRoot, "package.json"), "utf8"))
|
||||
packageJSON.devDependencies = Object.assign(packageJSON.devDependencies, {
|
||||
"svelte-check": "^2.0.0",
|
||||
"svelte-preprocess": "^4.0.0",
|
||||
"@rollup/plugin-typescript": "^8.0.0",
|
||||
"typescript": "^4.0.0",
|
||||
"tslib": "^2.0.0",
|
||||
"@tsconfig/svelte": "^2.0.0"
|
||||
})
|
||||
|
||||
// Add script for checking
|
||||
packageJSON.scripts = Object.assign(packageJSON.scripts, {
|
||||
"check": "svelte-check --tsconfig ./tsconfig.json"
|
||||
})
|
||||
|
||||
// Write the package JSON
|
||||
fs.writeFileSync(path.join(projectRoot, "package.json"), JSON.stringify(packageJSON, null, " "))
|
||||
|
||||
// mv src/main.js to main.ts - note, we need to edit rollup.config.js for this too
|
||||
const beforeMainJSPath = path.join(projectRoot, "src", "main.js")
|
||||
const afterMainTSPath = path.join(projectRoot, "src", "main.ts")
|
||||
fs.renameSync(beforeMainJSPath, afterMainTSPath)
|
||||
|
||||
// Switch the app.svelte file to use TS
|
||||
const appSveltePath = path.join(projectRoot, "src", "App.svelte")
|
||||
let appFile = fs.readFileSync(appSveltePath, "utf8")
|
||||
appFile = appFile.replace("<script>", '<script lang="ts">')
|
||||
appFile = appFile.replace("export let name;", 'export let name: string;')
|
||||
fs.writeFileSync(appSveltePath, appFile)
|
||||
|
||||
// Edit rollup config
|
||||
const rollupConfigPath = path.join(projectRoot, "rollup.config.js")
|
||||
let rollupConfig = fs.readFileSync(rollupConfigPath, "utf8")
|
||||
|
||||
// Edit imports
|
||||
rollupConfig = rollupConfig.replace(`'rollup-plugin-terser';`, `'rollup-plugin-terser';
|
||||
import sveltePreprocess from 'svelte-preprocess';
|
||||
import typescript from '@rollup/plugin-typescript';`)
|
||||
|
||||
// Replace name of entry point
|
||||
rollupConfig = rollupConfig.replace(`'src/main.js'`, `'src/main.ts'`)
|
||||
|
||||
// Add preprocessor
|
||||
rollupConfig = rollupConfig.replace(
|
||||
'compilerOptions:',
|
||||
'preprocess: sveltePreprocess({ sourceMap: !production }),\n\t\t\tcompilerOptions:'
|
||||
);
|
||||
|
||||
// Add TypeScript
|
||||
rollupConfig = rollupConfig.replace(
|
||||
'commonjs(),',
|
||||
'commonjs(),\n\t\ttypescript({\n\t\t\tsourceMap: !production,\n\t\t\tinlineSources: !production\n\t\t}),'
|
||||
);
|
||||
fs.writeFileSync(rollupConfigPath, rollupConfig)
|
||||
|
||||
// Add TSConfig
|
||||
const tsconfig = `{
|
||||
"extends": "@tsconfig/svelte/tsconfig.json",
|
||||
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules/*", "__sapper__/*", "public/*"]
|
||||
}`
|
||||
const tsconfigPath = path.join(projectRoot, "tsconfig.json")
|
||||
fs.writeFileSync(tsconfigPath, tsconfig)
|
||||
|
||||
// Add global.d.ts
|
||||
const dtsPath = path.join(projectRoot, "src", "global.d.ts")
|
||||
fs.writeFileSync(dtsPath, `/// <reference types="svelte" />`)
|
||||
|
||||
// Delete this script, but not during testing
|
||||
if (!argv[2]) {
|
||||
// Remove the script
|
||||
fs.unlinkSync(path.join(__filename))
|
||||
|
||||
// Check for Mac's DS_store file, and if it's the only one left remove it
|
||||
const remainingFiles = fs.readdirSync(path.join(__dirname))
|
||||
if (remainingFiles.length === 1 && remainingFiles[0] === '.DS_store') {
|
||||
fs.unlinkSync(path.join(__dirname, '.DS_store'))
|
||||
}
|
||||
|
||||
// Check if the scripts folder is empty
|
||||
if (fs.readdirSync(path.join(__dirname)).length === 0) {
|
||||
// Remove the scripts folder
|
||||
fs.rmdirSync(path.join(__dirname))
|
||||
}
|
||||
}
|
||||
|
||||
// Adds the extension recommendation
|
||||
fs.mkdirSync(path.join(projectRoot, ".vscode"), { recursive: true })
|
||||
fs.writeFileSync(path.join(projectRoot, ".vscode", "extensions.json"), `{
|
||||
"recommendations": ["svelte.svelte-vscode"]
|
||||
}
|
||||
`)
|
||||
|
||||
console.log("Converted to TypeScript.")
|
||||
|
||||
if (fs.existsSync(path.join(projectRoot, "node_modules"))) {
|
||||
console.log("\nYou will need to re-run your dependency manager to get started.")
|
||||
}
|
40
Server/app/svelte/src/components/cookies-dialog.svelte
Normal file
40
Server/app/svelte/src/components/cookies-dialog.svelte
Normal file
@@ -0,0 +1,40 @@
|
||||
<svelte:options tag="cookies-dialog" />
|
||||
|
||||
<script>
|
||||
|
||||
// Import libraries
|
||||
import { onMount } from 'svelte'
|
||||
|
||||
//Export
|
||||
|
||||
// Main code
|
||||
|
||||
|
||||
onMount(() => {
|
||||
|
||||
})
|
||||
</script>
|
||||
|
||||
<div id="wrapper">
|
||||
<div>
|
||||
<p>We use cookies to improve your experience, personalise your content and analyse site usage. By clicking “OK”, you agree to the use of cookies.</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<style>
|
||||
|
||||
@import '/css/common.css';
|
||||
|
||||
#wrapper {
|
||||
display: none;
|
||||
position: relative;
|
||||
height: 5rem;
|
||||
width: 100%;
|
||||
background: white;
|
||||
box-shadow: 0 0 0.314rem rgb(187, 187, 187);;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
107
Server/app/svelte/src/components/legend-component.svelte
Normal file
107
Server/app/svelte/src/components/legend-component.svelte
Normal file
@@ -0,0 +1,107 @@
|
||||
<svelte:options tag="legend-component" />
|
||||
|
||||
<script>
|
||||
|
||||
// Import libraries
|
||||
import { onMount } from 'svelte'
|
||||
import { pullLegendData } from '/js/predict/charts.js'
|
||||
|
||||
// Import components
|
||||
|
||||
//Export
|
||||
export let option = null
|
||||
export let chart = null
|
||||
export let data = {}
|
||||
|
||||
// Main code
|
||||
let legendData = []
|
||||
let buttons = []
|
||||
let dataKeys
|
||||
|
||||
function init() {
|
||||
if (option==null || option==undefined || chart==null || chart==undefined) {
|
||||
setTimeout(init,100)
|
||||
}
|
||||
else {
|
||||
legendData = pullLegendData(legendData,option)
|
||||
for (let obj of legendData) {
|
||||
data[obj.name] = true
|
||||
}
|
||||
dataKeys = Object.keys(data)
|
||||
}
|
||||
}
|
||||
|
||||
function toggleSeries(i) {
|
||||
data[dataKeys[i]] = !data[dataKeys[i]]
|
||||
let inds = []
|
||||
let ids = option.series.map((x) => x._id)
|
||||
let id = ids[i]
|
||||
for (let j=0;j<ids.length;j++) {
|
||||
if (ids[j]==id) {
|
||||
inds.push(j)
|
||||
}
|
||||
}
|
||||
for (let i of inds) {
|
||||
let series = option.series[i]
|
||||
if (!series.tooltip.show) {
|
||||
series.lineStyle.opacity = 1
|
||||
series.itemStyle.opacity = 1
|
||||
series.tooltip.show = true
|
||||
buttons[inds[0]].style.opacity = 1
|
||||
}
|
||||
else {
|
||||
series.lineStyle.opacity = 0
|
||||
series.itemStyle.opacity = 0
|
||||
series.tooltip.show = false
|
||||
buttons[inds[0]].style.opacity = 0.5
|
||||
}
|
||||
}
|
||||
|
||||
chart.setOption(option)
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
init()
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="legend">
|
||||
{#each legendData as item, i}
|
||||
<button bind:this={buttons[i]} on:click={() => toggleSeries(i)}>
|
||||
<div class="marker" style="background-color: {item.color}"></div>
|
||||
<span>{item.name}</span>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
|
||||
<style>
|
||||
@import '/css/common.css';
|
||||
@import '/css/test-basic.css';
|
||||
|
||||
button {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.legend {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.legend * {
|
||||
font-family: var(--sans-serif);
|
||||
}
|
||||
|
||||
.marker {
|
||||
position: relative;
|
||||
display:inline-block;
|
||||
margin-right: 0.5rem;
|
||||
top: 0.1rem;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
border-radius: 1rem;
|
||||
}
|
||||
</style>
|
27
Server/app/svelte/src/components/loadscreen-component.svelte
Normal file
27
Server/app/svelte/src/components/loadscreen-component.svelte
Normal file
@@ -0,0 +1,27 @@
|
||||
<svelte:options tag="loadscreen-component" />
|
||||
|
||||
<script>
|
||||
|
||||
// Import libraries
|
||||
import { onMount } from 'svelte'
|
||||
|
||||
// Import components
|
||||
|
||||
// Main code
|
||||
let loadscreen
|
||||
|
||||
onMount(() => {
|
||||
window.addEventListener('load', function() {
|
||||
loadscreen.parentNode.host.style.display = "none"
|
||||
})
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<div bind:this={loadscreen} id="loadscreen" style="width:100%; height: 100%; background:white; position: absolute; z-index: 100000"></div>
|
||||
|
||||
<style>
|
||||
|
||||
|
||||
|
||||
</style>
|
181
Server/app/svelte/src/components/pane-aligner.svelte
Normal file
181
Server/app/svelte/src/components/pane-aligner.svelte
Normal file
@@ -0,0 +1,181 @@
|
||||
<svelte:options tag="pane-aligner" />
|
||||
|
||||
<script>
|
||||
|
||||
// Import libraries
|
||||
import { getContext, setContext, onMount } from 'svelte'
|
||||
import { debounce } from "/js/libraries/miscTools.js"
|
||||
|
||||
// Import components
|
||||
|
||||
// Export statements
|
||||
|
||||
// Main code
|
||||
let root
|
||||
let mainPane
|
||||
let sidebarLeft
|
||||
let sidebarLeft2
|
||||
let sidebarRight
|
||||
let parentProps = getContext("alignerParent")
|
||||
let switchView = parentProps!=undefined ? getContext("alignerParent").switchView : undefined
|
||||
|
||||
let leftReplaced = false
|
||||
let left2Replaced = false
|
||||
let rightReplaced = false
|
||||
let switchViewReplaced = false
|
||||
|
||||
function adjustSlotted() {
|
||||
if (root.parentNode!=null) {
|
||||
let slotted = root.parentNode.host.childNodes
|
||||
if (slotted.length==0) {
|
||||
setTimeout(adjustSlotted, 50)
|
||||
}
|
||||
else {
|
||||
let changed = false
|
||||
let html = root.parentNode.innerHTML
|
||||
for (let item of slotted) {
|
||||
if (item.slot=="sidebar-left" && !leftReplaced) {
|
||||
html = html.replace("#sidebar-left{display:none}","")
|
||||
leftReplaced = true
|
||||
changed = true
|
||||
}
|
||||
else if (item.slot=="sidebar-left2" && !left2Replaced) {
|
||||
html = html.replace("#sidebar-left2{display:none}","")
|
||||
left2Replaced = true
|
||||
changed = true
|
||||
}
|
||||
else if (item.slot=="sidebar-right" && !rightReplaced){
|
||||
html = html.replace("#sidebar-right{display:none;","#sidebar-right{")
|
||||
rightReplaced = true
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
if (switchView!=undefined && !switchViewReplaced) {
|
||||
html = html.replace("1880px",switchView)
|
||||
changed = true
|
||||
}
|
||||
if (changed) {
|
||||
root.parentNode.innerHTML = html
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
window.addEventListener("resize", debounce(adjustSlotted,100))
|
||||
|
||||
onMount(() => {
|
||||
adjustSlotted()
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
<div bind:this={root} id="root" class="pane-centering">
|
||||
<div class="pane-container">
|
||||
<div id="sidebars-left" class="sidebar">
|
||||
<div bind:this={sidebarLeft} id="sidebar-left" class="pane">
|
||||
<slot name="sidebar-left"></slot>
|
||||
</div>
|
||||
<div bind:this={sidebarLeft2} id="sidebar-left2" class="pane">
|
||||
<slot name="sidebar-left2"></slot>
|
||||
</div>
|
||||
</div>
|
||||
<div bind:this={sidebarRight} id="sidebar-right" class="pane sidebar">
|
||||
<slot name="sidebar-right"></slot>
|
||||
</div>
|
||||
<div bind:this={mainPane} id="main-pane" class="pane">
|
||||
<slot name="main" id="main-slot"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<style>
|
||||
@import '/css/common.css';
|
||||
|
||||
.pane-container {
|
||||
display: block;
|
||||
margin-left: var(--total-margin-left,0rem);
|
||||
}
|
||||
|
||||
#root {
|
||||
min-height: var(--min-height,auto);
|
||||
}
|
||||
|
||||
#main-pane {
|
||||
position: relative;
|
||||
padding-left: var(--padding-left,0rem);
|
||||
padding-right: var(--padding-right,0rem);
|
||||
padding-top: var(--padding-top,0rem);
|
||||
padding-bottom: var(--padding-bottom,0rem);
|
||||
text-align: justify;
|
||||
background: var(--background,white);
|
||||
box-shadow: var(--box-shadow,0 0 0.314rem rgb(187, 187, 187));
|
||||
margin: auto;
|
||||
height: min-content;
|
||||
max-width: var(--width-main,66rem);
|
||||
width: var(--width-main,66rem);
|
||||
z-index: 1;
|
||||
overflow-x: var(--overflow-x,hidden);
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
#sidebars-left {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
margin-left: calc(-1*var(--width-left,22.5rem) - 1rem - 4rem);
|
||||
width: calc(var(--width-left,22.5rem) + 4rem);
|
||||
}
|
||||
|
||||
#sidebar-left,#sidebar-left2 {
|
||||
position: relative;
|
||||
background-color: white;
|
||||
padding: 2rem 2rem;
|
||||
}
|
||||
|
||||
#sidebar-left {
|
||||
display:none;
|
||||
}
|
||||
|
||||
#sidebar-left2 {
|
||||
display:none;
|
||||
}
|
||||
|
||||
#sidebar-right {
|
||||
display:none;
|
||||
margin-left: calc(var(--width-main,66rem) + 1rem);
|
||||
width: var(--width-right,auto);
|
||||
background-color: white;
|
||||
padding: 2rem 2rem;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1880px) {
|
||||
|
||||
#main-pane {
|
||||
max-width: initial;
|
||||
width: 100%;
|
||||
max-width: var(--width-main,66rem);
|
||||
padding-left: var(--padding-left-mobile,1.8rem);
|
||||
padding-right: var(--padding-right-mobile,1.8rem);
|
||||
padding-top: var(--padding-top-mobile,1.8rem);
|
||||
padding-bottom: var(--padding-bottom-mobile,1.8rem);
|
||||
}
|
||||
|
||||
#sidebars-left, #sidebar-right {
|
||||
position: relative;
|
||||
margin: auto;
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
max-width: var(--width-main,66rem);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.pane-container {
|
||||
width: 95%;
|
||||
justify-items: center;
|
||||
grid-auto-flow: row;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
256
Server/app/svelte/src/components/select-component.svelte
Normal file
256
Server/app/svelte/src/components/select-component.svelte
Normal file
@@ -0,0 +1,256 @@
|
||||
<svelte:options tag="select-component" />
|
||||
|
||||
<script>
|
||||
|
||||
// Import libraries
|
||||
import { onMount } from 'svelte'
|
||||
import { px2rem,getTextWidth,getCanvasFont } from "/js/libraries/miscTools.js"
|
||||
|
||||
//Export
|
||||
export let callback = null
|
||||
export let options = [""]
|
||||
export let value = null
|
||||
export let valueindex = null
|
||||
|
||||
// Main code
|
||||
let root = arguments[0]
|
||||
let select
|
||||
let optionsHolder
|
||||
let optionButtons = []
|
||||
let currentOption
|
||||
let currentOptionButton
|
||||
let init = false
|
||||
let key = 0
|
||||
let obs = null
|
||||
|
||||
$: setKeyValue(value,options)
|
||||
$: setKeyIndex(valueindex,options)
|
||||
$: changeOptionsWidth(select,optionsHolder)
|
||||
|
||||
function setKeyValue(value,options) {
|
||||
if (value!==null) {
|
||||
if (options.includes(value)) {
|
||||
let index = options.findIndex((element) => element==value)
|
||||
currentOption.innerHTML = value
|
||||
optionButtons[index].style.display = "none"
|
||||
valueindex = index
|
||||
}
|
||||
key += 1
|
||||
}
|
||||
}
|
||||
|
||||
function setKeyIndex(valueindex,options) {
|
||||
if (valueindex!=null && options!=undefined && options[valueindex]!=value) {
|
||||
value = options[valueindex]
|
||||
key += 1
|
||||
}
|
||||
}
|
||||
|
||||
function indexToValue(index) {
|
||||
value = options[index]
|
||||
}
|
||||
|
||||
function changeVisibility() {
|
||||
if (optionsHolder.style.display=="none") {
|
||||
optionsHolder.style.display = "initial"
|
||||
}
|
||||
else {
|
||||
optionsHolder.style.display = "none"
|
||||
}
|
||||
}
|
||||
|
||||
function changeOption(index,callback) {
|
||||
valueindex = index
|
||||
value = options[index]
|
||||
if (callback!=undefined && callback!=null) {
|
||||
callback(index)
|
||||
}
|
||||
}
|
||||
|
||||
function hideSelect() {
|
||||
optionsHolder.style.display = "none"
|
||||
}
|
||||
|
||||
function changeOptionsWidth(select,optionsHolder) {
|
||||
let selectWidth
|
||||
if (select!=undefined && optionsHolder!=undefined) {
|
||||
let selectWidthText = getComputedStyle(select).getPropertyValue('--width')
|
||||
if (isNaN(selectWidthText) || selectWidthText=="") {
|
||||
if (obs==null) {
|
||||
obs = new ResizeObserver(() => changeOptionsWidth(select,optionsHolder))
|
||||
obs.observe(currentOptionButton)
|
||||
return
|
||||
}
|
||||
else {
|
||||
selectWidthText = getComputedStyle(currentOptionButton).getPropertyValue('width')
|
||||
selectWidth = px2rem(parseFloat(selectWidthText.slice(0,selectWidthText.length-2)))
|
||||
}
|
||||
}
|
||||
else {
|
||||
selectWidth = parseFloat(selectWidthText.slice(0,selectWidthText.length-2))
|
||||
}
|
||||
let spanWidths = []
|
||||
for (let i=0;i<optionsHolder.children.length;i++) {
|
||||
let span = optionsHolder.children[i].children[0]
|
||||
let spanWidth = getTextWidth(span.innerHTML, getCanvasFont(span))
|
||||
spanWidths.push(spanWidth)
|
||||
}
|
||||
let maxOptionsWidth = px2rem(Math.max(...spanWidths))
|
||||
if (maxOptionsWidth>selectWidth) {
|
||||
let width = 1.1*maxOptionsWidth+"rem"
|
||||
optionsHolder.style.width = width
|
||||
for (let i=0;i<optionsHolder.children.length;i++) {
|
||||
let button = optionsHolder.children[i]
|
||||
button.style.width = width
|
||||
}
|
||||
optionsHolder.style.marginLeft = - (1.1*maxOptionsWidth - selectWidth) /2 - 0.05 + "rem"
|
||||
}
|
||||
else {
|
||||
let width = selectWidth + 0.1 + "rem"
|
||||
for (let i=0;i<optionsHolder.children.length;i++) {
|
||||
let button = optionsHolder.children[i]
|
||||
button.style.width = width
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
|
||||
init = true
|
||||
root.addEventListener('focusout', hideSelect)
|
||||
|
||||
})
|
||||
</script>
|
||||
|
||||
<div bind:this={select} class="select">
|
||||
{#key key}
|
||||
<button bind:this={currentOptionButton} id="current-option-button" on:click={changeVisibility}>
|
||||
<div id="current-options-div">
|
||||
<span bind:this={currentOption} id="current-options-span">{value!=null ? value : ""}</span>
|
||||
</div>
|
||||
<img id="arrow-down" src="../assets/arrow_down.svg" alt="arrow down">
|
||||
</button>
|
||||
<div bind:this={optionsHolder} id="options-holder" style="display: none">
|
||||
{#each options as option, i}
|
||||
<button bind:this={optionButtons[i]} value={i} on:click={() => changeOption(i,callback)}>
|
||||
<span>{option}</span>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
{/key}
|
||||
</div>
|
||||
|
||||
|
||||
<style>
|
||||
|
||||
@import '/css/common.css';
|
||||
|
||||
#current-options-div {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: calc(100% - 2.5rem);
|
||||
text-align: var(--text-align,left);
|
||||
padding-top: 0.5rem;
|
||||
}
|
||||
|
||||
#arrow-down {
|
||||
right: 0.5rem;
|
||||
width: 1.365rem;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
-webkit-transform: translateY(-50%);
|
||||
-ms-transform: translateY(-50%);
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.select {
|
||||
position: relative;
|
||||
margin-top: var(--margin-top,0);
|
||||
width: var(--width);
|
||||
max-width: var(--max-width);
|
||||
height: var(--height,2.75rem);
|
||||
border: var(--border,rgba(0,0,0,var(--opacity,1)) solid);
|
||||
border-width: var(--border-width, 0.063rem);
|
||||
border-radius: var(--border-radius,0.126rem);
|
||||
}
|
||||
|
||||
.select button {
|
||||
width: var(--width);
|
||||
max-width: var(--max-width);
|
||||
}
|
||||
|
||||
#current-option-button, #current-option-button * {
|
||||
opacity: var(--opacity,1);
|
||||
font-family: var(--font-family,var(--serif), serif);
|
||||
font-size: var(--font-size, 1.3rem);
|
||||
}
|
||||
|
||||
.select >:first-child {
|
||||
margin-right: -2.75rem;
|
||||
padding-right: 0.0rem;
|
||||
}
|
||||
|
||||
.select span {
|
||||
position: relative;
|
||||
padding-top: 0.5rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#current-option-button {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding-left: 0.341rem;
|
||||
font-weight: var(--font-weight,400);
|
||||
}
|
||||
|
||||
#options-holder {
|
||||
position: absolute;
|
||||
margin-top: calc(-1 * var(--border-width, 0.063rem));
|
||||
background: white;
|
||||
z-index: 1;
|
||||
margin-left: -0.05rem;
|
||||
border-radius: var(--border-radius-options,0.126rem);
|
||||
}
|
||||
|
||||
#options-holder * {
|
||||
font-size: var(--options-font-size, 1.2rem);
|
||||
font-family: var(--font-family,var(--serif), serif);
|
||||
}
|
||||
|
||||
#options-holder button {
|
||||
display: flex;
|
||||
position: relative;
|
||||
width: var(--width);
|
||||
background: white;
|
||||
font-weight: 400;
|
||||
text-align: var(--text-align,left);
|
||||
padding-top: 0.341rem;
|
||||
padding-bottom: 0.341rem;
|
||||
padding-right: 3.515rem;
|
||||
padding-left: 0.341rem;
|
||||
}
|
||||
|
||||
#options-holder button:hover {
|
||||
background: #cdcdcd;
|
||||
}
|
||||
|
||||
#options-holder button {
|
||||
border: black solid;
|
||||
border-width: 0 var(--border-width-options,var(--border-width, 0.063rem)) 0 var(--border-width-options,var(--border-width, 0.063rem));
|
||||
}
|
||||
|
||||
#options-holder >:first-child {
|
||||
border-top: black solid var(--border-width-options,var(--border-width, 0.063rem));
|
||||
border-top-left-radius: var(--border-radius-options,0.126rem);
|
||||
border-top-right-radius: var(--border-radius-options,0.126rem);
|
||||
}
|
||||
|
||||
#options-holder >:last-child {
|
||||
border-bottom: black solid var(--border-width-options,var(--border-width, 0.063rem));
|
||||
border-bottom-left-radius: var(--border-radius-options,0.126rem);
|
||||
border-bottom-right-radius: var(--border-radius-options,0.126rem);
|
||||
}
|
||||
|
||||
</style>
|
103
Server/app/svelte/src/components/switch-component.svelte
Normal file
103
Server/app/svelte/src/components/switch-component.svelte
Normal file
@@ -0,0 +1,103 @@
|
||||
<svelte:options tag="switch-component" />
|
||||
|
||||
<script>
|
||||
|
||||
// Import libraries
|
||||
import { onMount } from 'svelte'
|
||||
import { px2rem,getTextWidth,getCanvasFont } from "/js/libraries/miscTools.js"
|
||||
|
||||
//Export
|
||||
export let callback = null
|
||||
export let checked = false
|
||||
export const toggle = () => {
|
||||
let f = () => {
|
||||
if (callback != null) {
|
||||
checked = !checked
|
||||
callback()
|
||||
}
|
||||
else {
|
||||
toggle()
|
||||
}
|
||||
}
|
||||
setTimeout(f,100)
|
||||
}
|
||||
|
||||
function toggleClick() {
|
||||
if (callback != null) {
|
||||
checked = !checked
|
||||
callback()
|
||||
}
|
||||
}
|
||||
|
||||
// Main code
|
||||
|
||||
onMount(() => {
|
||||
|
||||
})
|
||||
</script>
|
||||
|
||||
<label class="switch">
|
||||
<input type="checkbox" bind:checked={checked} on:click={toggleClick}>
|
||||
<span class="switch-span"></span>
|
||||
</label>
|
||||
|
||||
|
||||
<style>
|
||||
|
||||
@import '/css/common.css';
|
||||
|
||||
.switch span {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: #ccc;
|
||||
-webkit-transition: .4s;
|
||||
transition: .4s;
|
||||
border-radius: calc(2* 1.2rem);
|
||||
}
|
||||
|
||||
.switch span:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: calc(var(--height) - 0.5rem);
|
||||
width: calc(var(--height) - 0.5rem);
|
||||
left: calc(0.3rem);
|
||||
bottom: 0.25rem;
|
||||
background-color: white;
|
||||
-webkit-transition: .4s;
|
||||
transition: .4s;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.switch input:checked + .switch-span {
|
||||
background-color: var(--pink);
|
||||
}
|
||||
|
||||
.switch input:hover + .switch-span {
|
||||
box-shadow: 0 0 0 var(--pink);
|
||||
}
|
||||
|
||||
.switch input:checked + .switch-span:before {
|
||||
-webkit-transform: translateX(calc(var(--width) - var(--height)/2 - 2*0.6rem));
|
||||
-ms-transform: translateX(calc(var(--width) - var(--height)/2 - 2*0.6rem));
|
||||
transform: translateX(calc(var(--width) - var(--height)/2 - 2*0.6rem));
|
||||
}
|
||||
|
||||
.switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: var(--width);
|
||||
height: var(--height);
|
||||
}
|
||||
|
||||
.switch input {
|
||||
position: absolute;
|
||||
width: var(--width);
|
||||
height: var(--height);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
</style>
|
34
Server/app/svelte/src/footer/footer-component.svelte
Normal file
34
Server/app/svelte/src/footer/footer-component.svelte
Normal file
@@ -0,0 +1,34 @@
|
||||
<svelte:options tag="footer-component" />
|
||||
|
||||
<script>
|
||||
|
||||
// Import components
|
||||
|
||||
</script>
|
||||
|
||||
<footer>
|
||||
<div id="footer-content-container">
|
||||
<div id="footer-grid-content-container" class="logged">
|
||||
<div id="contact-us-container">
|
||||
<h2>CONTACT US</h2>
|
||||
<p>Email: <a href="mailto:info@chiron.com">test@test</a></p>
|
||||
</div>
|
||||
</div>
|
||||
<button on:click={() => {location.href='#'}} id="footer-up" aria-label="go up">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="42.545" height="72.601" viewBox="0 0 42.545 72.601">
|
||||
<g id="Group_268" data-name="Group 268" transform="translate(-6.177 -2.399)">
|
||||
<rect id="Rectangle_146" data-name="Rectangle 146" width="11" height="51" rx="5.5" transform="translate(22 24)" fill="var(--pink)"/>
|
||||
<path id="Path_1145" data-name="Path 1145" d="M23.814,4.021a5,5,0,0,1,7.372,0l16.134,17.6c2.94,3.207,1.046,10.4-3.686,8.379S28.02,14.081,28.391,13.524,16.544,27.976,11.366,30,4.741,24.828,7.68,21.621Z" fill="var(--pink)"/>
|
||||
</g>
|
||||
</svg>
|
||||
</button>
|
||||
<p id="footer-copyright">© 2023 LibSoc</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<style>
|
||||
|
||||
@import '/css/common.css';
|
||||
@import '/css/footer.css';
|
||||
|
||||
</style>
|
26
Server/app/svelte/src/landing-component.svelte
Normal file
26
Server/app/svelte/src/landing-component.svelte
Normal file
@@ -0,0 +1,26 @@
|
||||
<svelte:options tag="landing-component" />
|
||||
|
||||
<script>
|
||||
// Import statements
|
||||
i
|
||||
|
||||
// Import components
|
||||
|
||||
|
||||
// Main code
|
||||
|
||||
|
||||
|
||||
onMount(() => {
|
||||
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--HTML GOES HERE-->
|
||||
<p>I AM A TEXT FOR TESTING</p>
|
||||
|
||||
<style>
|
||||
@import '/css/common.css';
|
||||
|
||||
|
||||
</style>
|
57
Server/app/svelte/src/navbar/navbar-component.svelte
Normal file
57
Server/app/svelte/src/navbar/navbar-component.svelte
Normal file
@@ -0,0 +1,57 @@
|
||||
<svelte:options tag="navbar-component" />
|
||||
|
||||
<script>
|
||||
|
||||
// Import statements
|
||||
import { onMount } from 'svelte'
|
||||
|
||||
// Main code
|
||||
let hambInput
|
||||
let navbar
|
||||
|
||||
function changeNavbar() {
|
||||
if (hambInput.checked) {
|
||||
navbar.style.background = "white"
|
||||
navbar.style.boxShadow = "0 0 0.314rem rgb(187, 187, 187)"
|
||||
}
|
||||
else {
|
||||
setTimeout(()=> {
|
||||
navbar.style.position = "relative"
|
||||
navbar.style.background = ""
|
||||
navbar.style.boxShadow = ""
|
||||
}
|
||||
,510)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<!-- Navigation bar -->
|
||||
<header bind:this={navbar} id="navbar">
|
||||
<!-- Logo -->
|
||||
<a id=logo-container href="/">
|
||||
<img src="" id="navbar-logo" alt="iql logo">
|
||||
<span id="navbar-logo-text">LibSoc</span>
|
||||
</a>
|
||||
<!-- Hamburger icon -->
|
||||
<input bind:this={hambInput} type="checkbox" id="side-menu" on:click={changeNavbar}>
|
||||
<label id="hamb" for="side-menu"><span id="hamb-line"></span></label>
|
||||
<!-- Menu -->
|
||||
<nav id="nav">
|
||||
<ul id="menu">
|
||||
<li><a href="/test">test</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<style>
|
||||
|
||||
@import '/css/common.css';
|
||||
@import '/css/navbar.css';
|
||||
|
||||
</style>
|
Reference in New Issue
Block a user