Add loaders.

This commit is contained in:
Madeorsk 2024-07-06 16:52:26 +02:00
parent e15861393e
commit bc79307ff7
Signed by: Madeorsk
SSH key fingerprint: SHA256:J9G0ofIOLKf7kyS2IfrMqtMaPdfsk1W02+oGueZzDDU
10 changed files with 212 additions and 9 deletions

View file

@ -11,6 +11,9 @@ import {Tooltip} from "../src/Components/Floating/Tooltip";
import {DatepickerInput} from "../src/Components/Forms/DatepickerInput"; import {DatepickerInput} from "../src/Components/Forms/DatepickerInput";
import {TimepickerInput} from "../src/Components/Forms/TimepickerInput"; import {TimepickerInput} from "../src/Components/Forms/TimepickerInput";
import {Select} from "../src/Components/Select/Select"; import {Select} from "../src/Components/Select/Select";
import {SpinningLoader} from "../src/Components/Loaders/SpinningLoader";
import {ListLoader} from "../src/Components/Loaders/ListLoader";
import {GenericLoader} from "../src/Components/Loaders/GenericLoader";
export function DemoApp() export function DemoApp()
{ {
@ -25,7 +28,6 @@ export function DemoApp()
<h2>TODO</h2> <h2>TODO</h2>
<ul> <ul>
<li>Loaders</li>
<li>Dropdown menus</li> <li>Dropdown menus</li>
<li>Main menu</li> <li>Main menu</li>
<li>Tabs / Apps selectors</li> <li>Tabs / Apps selectors</li>
@ -57,20 +59,20 @@ export function DemoApp()
<button type={"button"}>A cool button</button> <button type={"button"}>A cool button</button>
<a className={"button"} href={"#"}>A link button</a> <a className={"button"} href={"#"}>A link button</a>
<button type={"button"} className={"flat"}>A flat button</button> <button type={"button"} className={"flat"}>A flat button</button>
<button type={"button"} className={"validation"}><FloppyDisk weight={"bold"} /> A validation button</button> <button type={"button"} className={"validation"}><FloppyDisk weight={"bold"}/> A validation button</button>
<button type={"button"} className={"cancel"}><XCircle weight={"bold"} /> A cancellation button</button> <button type={"button"} className={"cancel"}><XCircle weight={"bold"}/> A cancellation button</button>
<button type={"button"} className={"delete"}><TrashSimple weight={"bold"} /> A deletion button</button> <button type={"button"} className={"delete"}><TrashSimple weight={"bold"}/> A deletion button</button>
<h2>Forms</h2> <h2>Forms</h2>
<form> <form>
<label> <label>
Text label <RequiredField /> Text label <RequiredField/>
<input type={"text"} placeholder={"Normal demo text"} required={true} /> <input type={"text"} placeholder={"Normal demo text"} required={true}/>
</label> </label>
<label> <label>
Textarea label <RequiredField /> Textarea label <RequiredField/>
<textarea placeholder={"A normal textarea."} required={true}></textarea> <textarea placeholder={"A normal textarea."} required={true}></textarea>
</label> </label>
@ -110,7 +112,8 @@ export function DemoApp()
<a href={"#"}>Link test</a> <a href={"#"}>Link test</a>
</div> </div>
<p> <p>
<strong>Lorem ipsum</strong> dolor <code>sit amet</code>, <em>consectetur</em> adipiscing elit <a href={"https://aleph.land"} target={"_blank"}>aleph</a>. Donec accumsan <strong>Lorem ipsum</strong> dolor <code>sit amet</code>, <em>consectetur</em> adipiscing elit <a
href={"https://aleph.land"} target={"_blank"}>aleph</a>. Donec accumsan
pulvinar felis, vitae eleifend augue lacinia tempus. Integer nec iaculis ante. Duis a quam urna. Nullam pulvinar felis, vitae eleifend augue lacinia tempus. Integer nec iaculis ante. Duis a quam urna. Nullam
tincidunt rutrum felis, a efficitur enim facilisis sit amet. Quisque dictum semper sagittis. Maecenas in orci tincidunt rutrum felis, a efficitur enim facilisis sit amet. Quisque dictum semper sagittis. Maecenas in orci
hendrerit, tempor nunc non, tempus mi. Praesent blandit varius rutrum. Nullam quis mauris eros. Vestibulum hendrerit, tempor nunc non, tempus mi. Praesent blandit varius rutrum. Nullam quis mauris eros. Vestibulum
@ -226,7 +229,7 @@ export function DemoApp()
<button>Click me!</button> <button>Click me!</button>
</Float> </Float>
<Float mode={"always"} content={"I am always shown."} floatingOptions={{ placement: "top" }}> <Float mode={"always"} content={"I am always shown."} floatingOptions={{placement: "top"}}>
<button>Why always me?</button> <button>Why always me?</button>
</Float> </Float>
@ -245,6 +248,21 @@ export function DemoApp()
</Card> </Card>
<h2>Loaders</h2> <h2>Loaders</h2>
<h3>Simple loaders</h3>
<Card>
<SpinningLoader/>
<ListLoader/>
</Card>
<h3>Generic loader</h3>
<GenericLoader>
<Card>
Sample content.
</Card>
</GenericLoader>
</main> </main>
); );
} }

View file

@ -0,0 +1,10 @@
import React from "react";
export function GenericLoader({children}: React.PropsWithChildren<{}>)
{
return (
<div className={"generic loader"}>
{children}
</div>
);
}

View file

@ -0,0 +1,37 @@
import React, {useCallback} from "react";
export function ListLoader({count, itemContent}: {
/**
* Sample items count.
* 3 by default.
*/
count?: number;
/**
* Sample items content or content generator function.
*/
itemContent?: React.ReactNode|((key: number) => React.ReactNode);
})
{
// Sample items count. 3 by default.
count = count === undefined ? 3 : count;
// Initialize the sample content generator.
const contentGenerator = useCallback((key: number) => (
typeof itemContent != "function"
// No function given, return the given content or a sample text.
? (itemContent ?? "Loading content...")
// A function have been given, just return its result.
: itemContent(key)
), [itemContent]);
return (
<ul className={"list loader"}>
{ // Render every sample item.
[...Array(count)].map((_, key) => (
<li key={key}><div className={"content"}>{contentGenerator(key)}</div></li>
))
}
</ul>
);
}

View file

@ -0,0 +1,8 @@
import React from "react";
export function SpinningLoader()
{
return (
<div className={"spinning loader"}></div>
);
}

View file

@ -7,6 +7,7 @@
@import "components/_headings"; @import "components/_headings";
@import "components/_link"; @import "components/_link";
@import "components/_list"; @import "components/_list";
@import "components/_loaders";
@import "components/_select"; @import "components/_select";
@import "components/_steps"; @import "components/_steps";
@import "components/_table"; @import "components/_table";

View file

@ -0,0 +1,5 @@
@import "loaders/_animated-background";
@import "loaders/_generic";
@import "loaders/_list";
@import "loaders/_spinning";

View file

@ -0,0 +1,21 @@
@loaders-animated-background-color: linear-gradient(111deg, var(--background-darkest) 0%, var(--background-lightest) 50%, var(--background-darkest) 100%), var(--background-darkest);
@keyframes loaders-animated-background
{
0%
{
background: @loaders-animated-background-color 10% 0;
background-size: 300% 300%;
}
50%
{
background-position: 91% 100%;
}
100%
{
background-size: 300% 300%;
background-position: 10% 0;
}
}

View file

@ -0,0 +1,13 @@
.generic.loader
{
margin: auto;
width: fit-content;
border-radius: 0.25em;
border: solid var(--background) thin;
background: @loaders-animated-background-color;
color: transparent;
* { background: none; color: transparent; border: none; box-shadow: 0 0 0 0 transparent; outline: none; }
animation: loaders-animated-background 4s linear infinite alternate;
}

View file

@ -0,0 +1,49 @@
ul.list.loader
{
margin: 1em auto;
padding: 0;
list-style: none;
> li
{
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: stretch;
margin: 1em auto;
&::before
{
content: "";
display: inline-block;
align-self: center;
margin: 0 0.66em 0 0;
width: 2em;
height: 2em;
border-radius: 2em;
border: solid var(--background) thin;
background: @loaders-animated-background-color;
animation: loaders-animated-background 4s linear infinite alternate;
vertical-align: middle;
}
.content
{
flex: 1;
position: relative;
padding: 0.5em 1em;
border-radius: 2em;
border: solid var(--background) thin;
background: @loaders-animated-background-color;
color: transparent;
animation: loaders-animated-background 4s linear infinite alternate;
}
}
}

View file

@ -0,0 +1,41 @@
.spinning.loader
{
position: relative;
display: inline-block;
margin: 1em;
width: 2.5em;
height: 2.5em;
border-radius: 50%;
box-sizing: border-box;
border: 0.3em solid var(--foreground);
animation: spinning-loader-rotation 1s linear infinite;
&::after
{
content: "";
box-sizing: border-box;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 3.5em;
height: 3.5em;
border-radius: 50%;
border: 0.3em solid transparent;
border-bottom-color: var(--primary);
}
}
@keyframes spinning-loader-rotation
{
0%
{
transform: rotate(0deg);
}
100%
{
transform: rotate(360deg);
}
}