mirror of
https://github.com/imputnet/cobalt.git
synced 2026-04-20 20:08:05 +00:00
112 lines
2.5 KiB
Svelte
112 lines
2.5 KiB
Svelte
<script lang="ts">
|
|
import IconLink from "@tabler/icons-svelte/IconLink.svelte";
|
|
import IconLoader2 from "@tabler/icons-svelte/IconLoader2.svelte";
|
|
|
|
type Props = {
|
|
loading: boolean;
|
|
};
|
|
|
|
let { loading }: Props = $props();
|
|
|
|
let animated = $state(false);
|
|
|
|
$effect(() => {
|
|
animated = loading;
|
|
});
|
|
|
|
/*
|
|
initial spinner state is equal to loading state,
|
|
just so it's animated on init (or not).
|
|
on transition start, it overrides the value
|
|
to start spinning (to prevent zooming in with no spinning).
|
|
|
|
then, on transition end, when the spinner is hidden,
|
|
and if loading state is false, the class is removed
|
|
and the spinner doesn't spin in background while being invisible.
|
|
|
|
if loading state is true, then it will just stay spinning
|
|
(aka when it's visible and should be spinning).
|
|
|
|
the spin on transition start is needed for the whirlpool effect
|
|
of the link icon being sucked into the spinner.
|
|
|
|
this may be unnecessarily complicated but i think it looks neat.
|
|
*/
|
|
</script>
|
|
|
|
<div id="input-icons" class:loading>
|
|
<div
|
|
class="input-icon spinner-icon"
|
|
class:animated
|
|
ontransitionstart={() => (animated = true)}
|
|
ontransitionend={() => (animated = loading)}
|
|
>
|
|
<IconLoader2 />
|
|
</div>
|
|
<div class="input-icon link-icon">
|
|
<IconLink />
|
|
</div>
|
|
</div>
|
|
|
|
<style>
|
|
#input-icons,
|
|
#input-icons :global(svg),
|
|
.input-icon {
|
|
width: 18px;
|
|
height: 18px;
|
|
}
|
|
|
|
#input-icons {
|
|
display: flex;
|
|
position: absolute;
|
|
margin-left: var(--input-padding);
|
|
pointer-events: none;
|
|
}
|
|
|
|
:global([dir="rtl"]) #input-icons {
|
|
margin-left: unset;
|
|
margin-right: var(--input-padding);
|
|
}
|
|
|
|
#input-icons :global(svg) {
|
|
stroke: var(--gray);
|
|
stroke-width: 2px;
|
|
will-change: transform;
|
|
}
|
|
|
|
.input-icon {
|
|
position: absolute;
|
|
transition:
|
|
transform 0.25s,
|
|
opacity 0.25s;
|
|
}
|
|
|
|
.link-icon {
|
|
transform: none;
|
|
opacity: 1;
|
|
}
|
|
|
|
.spinner-icon {
|
|
transform: scale(0.4);
|
|
opacity: 0;
|
|
}
|
|
|
|
.spinner-icon.animated :global(svg) {
|
|
animation: spinner 0.7s infinite linear;
|
|
}
|
|
|
|
.loading .link-icon :global(svg) {
|
|
animation: spinner 0.7s linear;
|
|
}
|
|
|
|
.loading .link-icon {
|
|
transform: scale(0.4);
|
|
opacity: 0;
|
|
}
|
|
|
|
.loading .spinner-icon {
|
|
transform: none;
|
|
opacity: 1;
|
|
}
|
|
</style>
|