aboutsummaryrefslogtreecommitdiff
path: root/src_frontend/ComponentLib
diff options
context:
space:
mode:
authorJakob Stendahl <jakob.stendahl@outlook.com>2021-09-19 19:43:11 +0200
committerJakob Stendahl <jakob.stendahl@outlook.com>2021-09-19 19:43:11 +0200
commit7bdce37fd3f18e2712e18c4e2c64cac69af0aca1 (patch)
treeb7ad3f1cca92e2dfd2664ae9e65652bd03ff58b2 /src_frontend/ComponentLib
parente6880cd8ccf82d993f222cb14b4860581654acb8 (diff)
downloadLuxcena-Neo-7bdce37fd3f18e2712e18c4e2c64cac69af0aca1.tar.gz
Luxcena-Neo-7bdce37fd3f18e2712e18c4e2c64cac69af0aca1.zip
:boom: Introduce new UI based on svelte, and rewrite a lot of the node app and the NeoRuntime
Diffstat (limited to 'src_frontend/ComponentLib')
-rw-r--r--src_frontend/ComponentLib/Button/Button.svelte80
-rw-r--r--src_frontend/ComponentLib/Button/FloatingButton.svelte104
-rw-r--r--src_frontend/ComponentLib/FloatingSelect.svelte45
-rw-r--r--src_frontend/ComponentLib/Input.svelte14
-rw-r--r--src_frontend/ComponentLib/PrettyVar.svelte16
-rw-r--r--src_frontend/ComponentLib/RoundRange.svelte169
6 files changed, 428 insertions, 0 deletions
diff --git a/src_frontend/ComponentLib/Button/Button.svelte b/src_frontend/ComponentLib/Button/Button.svelte
new file mode 100644
index 0000000..3943ecf
--- /dev/null
+++ b/src_frontend/ComponentLib/Button/Button.svelte
@@ -0,0 +1,80 @@
+<script>
+ export let faIcon = false;
+ export let fullWidth = false;
+ export let backgroundColor = "var(--theme-primary)";
+ export let color = "var(--theme-on-primary)";
+
+ export let loadingPromise = null;
+ $: listen(loadingPromise);
+ function listen(promise) {
+ if (promise != null) {
+ loading = true;
+ promise.then(res => {
+ loading = false;
+ success = true;
+ }).catch(err => {
+ loading = false;
+ success = false;
+ });
+ } else {
+ loading = false;
+ }
+ }
+ let loading;
+ let success;
+</script>
+
+<style>
+ button {
+ background-color: var(--bg-color);
+ color: var(--color);
+ border: none;
+ text-decoration: none;
+ padding: 5px 15px;
+ font-size: 15px;
+
+ transition: background-color, color 0.1s ease;
+ border-radius: 15px;
+ }
+ button:hover {
+ filter: brightness(0.95);
+ }
+ button:active {
+ filter: brightness(0.90);
+ }
+ .fullWidth {
+ width: 100%;
+ }
+ .iconButton {
+ display: flex;
+ }
+ .iconButton .text {
+ margin: auto;
+ }
+ .active {
+ background-color: var(--active-bg-color);
+ color: var(--active-color);
+ }
+</style>
+
+<button
+ on:click
+ class:fullWidth={fullWidth}
+ class:iconButton={faIcon != false}
+ style="--bg-color: {backgroundColor};
+ --color: {color};">
+
+ {#if faIcon}
+ <div class="icon">
+ <i class={faIcon}></i>
+ </div>
+ {/if}
+
+ {#if loading}
+ <i class="fas fa-spinner fa-pulse"></i>
+ {:else}
+ <div class="text">
+ <slot></slot>
+ </div>
+ {/if}
+</button> \ No newline at end of file
diff --git a/src_frontend/ComponentLib/Button/FloatingButton.svelte b/src_frontend/ComponentLib/Button/FloatingButton.svelte
new file mode 100644
index 0000000..123debc
--- /dev/null
+++ b/src_frontend/ComponentLib/Button/FloatingButton.svelte
@@ -0,0 +1,104 @@
+<script>
+ export let faIcon = false;
+ export let fullWidth = false;
+ export let backgroundColor = "white";
+ export let color = "black";
+ export let activeBackgroundColor = "gray";
+ export let activeColor = "white";
+ export let active = false;
+ export let label = null;
+
+ export let loadingPromise = null;
+ $: listen(loadingPromise);
+ function listen(promise) {
+ if (promise != null) {
+ loading = true;
+ promise.then(res => {
+ loading = false;
+ success = true;
+ }).catch(err => {
+ loading = false;
+ success = false;
+ });
+ } else {
+ loading = false;
+ }
+ }
+ let loading;
+ let success;
+</script>
+
+<style>
+ button {
+ background-color: var(--bg-color);
+ color: var(--color);
+ border: none;
+ text-decoration: none;
+ padding: 15px;
+ font-size: 15px;
+
+ transition: background-color, color 0.1s ease;
+ border-radius: 50px;
+ }
+ button:hover {
+ filter: brightness(0.95);
+ }
+ button:active {
+ filter: brightness(0.90);
+ }
+ .fullWidth {
+ width: 100%;
+ }
+ .iconButton {
+ display: flex;
+ }
+ .iconButton .text {
+ margin: auto;
+ }
+ .active {
+ background-color: var(--active-bg-color);
+ color: var(--active-color);
+ }
+ .wrapper {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ }
+ .label {
+ margin-top: 10px;
+ color: var(--grey-700);
+ font-size: 13px;
+ }
+</style>
+
+<div class="wrapper">
+ <button
+ on:click
+ class:fullWidth={fullWidth}
+ class:iconButton={faIcon != false}
+ class:active={active}
+ style="--bg-color: {backgroundColor};
+ --color: {color};
+ --active-bg-color: {activeBackgroundColor};
+ --active-color: {activeColor};"
+ class="drop-shadow"
+ >
+
+ {#if faIcon}
+ <div class="icon">
+ <i class={faIcon}></i>
+ </div>
+ {/if}
+
+ {#if loading}
+ <i class="fas fa-spinner fa-pulse"></i>
+ {:else}
+ <div class="text">
+ <slot></slot>
+ </div>
+ {/if}
+ </button>
+ {#if label != null}
+ <span class="label">{label}</span>
+ {/if}
+</div> \ No newline at end of file
diff --git a/src_frontend/ComponentLib/FloatingSelect.svelte b/src_frontend/ComponentLib/FloatingSelect.svelte
new file mode 100644
index 0000000..0d71ca7
--- /dev/null
+++ b/src_frontend/ComponentLib/FloatingSelect.svelte
@@ -0,0 +1,45 @@
+<script>
+ import FloatingButton from "./Button/FloatingButton.svelte";
+ export let value;
+
+ export let faIcon = false;
+ export let backgroundColor = "white";
+ export let color = "black";
+ export let activeBackgroundColor = "gray";
+ export let activeColor = "white";
+ export let active = false;
+ export let label = null;
+</script>
+
+<style>
+ select {
+ text-decoration: none;
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none;
+ background-color: transparent;
+ border: none;
+ margin: auto;
+ text-align: center;
+ }
+ select:focus {
+ outline: none;
+ }
+ .active {
+ background-color: var(--active-bg-color);
+ color: var(--active-color);
+ }
+</style>
+<FloatingButton fullWidth=true
+ {faIcon}
+ {label}
+ {backgroundColor}
+ {color}
+ {activeBackgroundColor}
+ {activeColor}
+ {active} >
+ <select on:change
+ bind:value={value}>
+ <slot></slot>
+ </select>
+</FloatingButton> \ No newline at end of file
diff --git a/src_frontend/ComponentLib/Input.svelte b/src_frontend/ComponentLib/Input.svelte
new file mode 100644
index 0000000..ecbaefb
--- /dev/null
+++ b/src_frontend/ComponentLib/Input.svelte
@@ -0,0 +1,14 @@
+<script>
+ export let fullWidth = true;
+ export let type = "text";
+ export let id;
+ export let value
+</script>
+
+<style>
+
+</style>
+
+<div>
+ <input type={type} id={id} bind:value={value} />
+</div> \ No newline at end of file
diff --git a/src_frontend/ComponentLib/PrettyVar.svelte b/src_frontend/ComponentLib/PrettyVar.svelte
new file mode 100644
index 0000000..68f082f
--- /dev/null
+++ b/src_frontend/ComponentLib/PrettyVar.svelte
@@ -0,0 +1,16 @@
+<script>
+ export let varText;
+ let prettyVarText;
+
+ function prettify(text) {
+ try {
+ prettyVarText = text.replace("_", " ");
+ prettyVarText = prettyVarText.charAt(0).toUpperCase() + prettyVarText.slice(1);
+ } catch {
+ prettyVarText = varText;
+ }
+ }
+ $: prettify(varText);
+</script>
+
+{prettyVarText}
diff --git a/src_frontend/ComponentLib/RoundRange.svelte b/src_frontend/ComponentLib/RoundRange.svelte
new file mode 100644
index 0000000..42c4d57
--- /dev/null
+++ b/src_frontend/ComponentLib/RoundRange.svelte
@@ -0,0 +1,169 @@
+<script>
+ import { onMount } from 'svelte';
+ import { createEventDispatcher } from 'svelte';
+ const dispatch = createEventDispatcher();
+
+ export let min = 0;
+ export let max = 100;
+ export let value = 0;
+ export let enabled = true;
+
+ let svg;
+ let indicator;
+ let thumb;
+ let track;
+ let cVal = value;
+ let touchActive = false;
+
+ let inputCoordinates;
+
+ function setValue(_value) {
+ value = _value;
+ let delta = cVal - _value;
+
+ if (!touchActive && (Math.abs(delta) > 10)) {
+ let animInterval = setInterval(() => {
+ cVal = cVal + ((delta < 0) ? 1 : -1);
+ setThumbAndTrack(cVal);
+ if ( ((delta < 0) && (cVal >= value)) || ((delta > 0) && (cVal <= value)) ) {
+ clearInterval(animInterval);
+ }
+ }, 1);
+ } else {
+ setThumbAndTrack(value);
+ cVal = value;
+ }
+ }
+
+ function setThumbAndTrack(val_pos) {
+ if (indicator && thumb) {
+ let indicator_value = (((val_pos - min) * 207) / (max - min));
+ indicator.style.strokeDasharray = `${indicator_value},207`;
+
+ let p = indicator.getPointAtLength(indicator_value);
+ thumb.setAttribute("transform", `translate(${p.x}, ${p.y})`);
+ }
+ }
+
+ onMount(async() => {
+ let lastVal = value;
+ setValue(value);
+ setInterval(() => {
+ if (value != lastVal) {
+ setValue(value);
+ lastVal = value;
+ }
+ }, 500);
+ window.indicator = indicator;
+
+ inputCoordinates = svg.createSVGPoint();
+ addEventListenersToElement(thumb);
+ addEventListenersToElement(track);
+ addEventListenersToElement(indicator);
+ });
+
+ function addEventListenersToElement(el) {
+ el.addEventListener("touchstart", () => {
+ touchActive = true;
+ }, { capture: true, passive: false });
+ el.addEventListener("touchend", () => {
+ touchActive = false;
+ }, { capture: true, passive: true });
+ el.addEventListener("touchmove", updateValueFromTouchEvent, { capture: true, passive: false });
+
+ el.addEventListener("mousedown", (ev) => {
+ touchActive = true;
+ updateValueFromMouseEvent(ev);
+ svg.addEventListener("mousemove", updateValueFromMouseEvent);
+ }, { capture: true, passive: false });
+ el.addEventListener("mouseup", (ev) => {
+ touchActive = false;
+ svg.removeEventListener("mousemove", updateValueFromMouseEvent);
+ }, { capture: true, passive: false });
+ }
+
+ function updateValueFromTouchEvent(ev) {
+ inputCoordinates.x = ev.touches[0].clientX;
+ inputCoordinates.y = ev.touches[0].clientY;
+ updateValueFromPoint(inputCoordinates);
+ ev.preventDefault();
+ }
+
+ function updateValueFromMouseEvent(ev) {
+ inputCoordinates.x = ev.clientX;
+ inputCoordinates.y = ev.clientY;
+ updateValueFromPoint(inputCoordinates);
+ ev.preventDefault();
+ }
+
+ function updateValueFromPoint(point) {
+ inputCoordinates = inputCoordinates.matrixTransform(svg.getScreenCTM().inverse());
+ setValue(getValueAtLength(indicator, getPathLangthOfClosestPoint(indicator, inputCoordinates)));
+ dispatch("change");
+ }
+
+ function getPathLangthOfClosestPoint(path, point) {
+ let pathLength = path.getTotalLength();
+ let shortestDistance = Number.MAX_VALUE;
+ let shortestPathDistance;
+
+ for (let i = 0; i <= pathLength; i += 4) {
+ let p = path.getPointAtLength(i),
+ dx = p.x - point.x,
+ dy = p.y - point.y,
+ cDistance = dx * dx + dy * dy;
+
+ if (cDistance < shortestDistance) {
+ shortestDistance = cDistance;
+ shortestPathDistance = i;
+ }
+ }
+
+ return shortestPathDistance;
+ }
+
+ function getValueAtLength(path, length) {
+ return Math.floor(((length * (max - min)) / path.getTotalLength()) + min);
+ }
+</script>
+
+<style>
+ svg {
+ --primary-color: var(--yellow-500);
+ --muted-color: var(--yellow-100);
+ margin-top: -15px;
+ }
+ path {
+ transition: stroke 0.2s ease;
+ }
+ circle {
+ filter: drop-shadow(0 0 2px #d0d0d0);
+ transition: stroke 0.2s ease;
+ }
+ circle:hover {
+ stroke: #f9f9f9;
+ }
+ circle:active {
+ fill: var(--yellow-600);
+ }
+ .disabled {
+ --primary-color: var(--grey-200);
+ --muted-color: var(--grey-50);
+ }
+</style>
+
+
+<svg bind:this={svg} class:disabled={!enabled} viewbox="0 0 100 100">
+ <path bind:this={track} fill="none" stroke-linecap="round" stroke-width="10" stroke="var(--muted-color)"
+ d="M25 85
+ a 40 40 0 1 1 50 0
+ ">
+ </path>
+ <path bind:this={indicator} fill="none" stroke-linecap="round" stroke-width="10" stroke="var(--primary-color)"
+ stroke-dasharray="207,0"
+ d="M25 85
+ a 40 40 0 1 1 50 0">
+ </path>
+ <circle bind:this={thumb} cx="0" cy="0" r="8" stroke="white" stroke-width="3" fill="var(--primary-color)" />
+ <text id="count" x="50" y="55" text-anchor="middle" dy="7" font-size="20">{value}%</text>
+</svg> \ No newline at end of file