Redesign Huly loading screen (#10189)

* Redesign Huly loading screen

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* Update copyright

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* Use square spinner

Signed-off-by: Artem Savchenko <armisav@gmail.com>

---------

Signed-off-by: Artem Savchenko <armisav@gmail.com>
This commit is contained in:
Artyom Savchenko
2025-11-07 08:37:51 +07:00
committed by GitHub
parent e10a05b42e
commit f96acb2b1c
6 changed files with 432 additions and 5 deletions
@@ -0,0 +1,53 @@
<!--
// Copyright © 2025 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
import { createEventDispatcher, onMount } from 'svelte'
import SquareSpinner from './icons/SquareSpinner.svelte'
export let shrink: boolean = false
export let label: string = ''
export let size: 'small' | 'medium' | 'large' = 'small'
const dispatch = createEventDispatcher()
let timer: any
onMount(() => {
timer = setTimeout(() => {
dispatch('progress')
}, 50)
return () => {
clearTimeout(timer)
}
})
</script>
<div class="spinner-container" class:fullSize={!shrink}>
<div data-label={label} class="flex-row-center flex-gap-2" class:labeled={label !== ''}>
<SquareSpinner {size} />
<slot />
</div>
</div>
<style lang="scss">
.spinner-container {
display: flex;
justify-content: center;
align-items: center;
&.fullSize {
width: 100%;
height: 100%;
}
}
</style>
+7 -1
View File
@@ -19,6 +19,7 @@
import type { AnyComponent, AnySvelteComponent } from '../types'
import ErrorPresenter from './ErrorPresenter.svelte'
import Loading from './Loading.svelte'
import AppLoading from './AppLoading.svelte'
import ErrorBoundary from './internal/ErrorBoundary'
import { clone } from '@hcengineering/core'
@@ -30,6 +31,7 @@
export let showLoading = true
export let inline: boolean = false
export let disabled: boolean = false
export let appLoading: boolean = false
let _is: AnyComponent | AnySvelteComponent = is
@@ -105,7 +107,11 @@
{#if _is != null}
{#if loading}
{#if showLoading}
<Loading {shrink} />
{#if appLoading}
<AppLoading {shrink} />
{:else}
<Loading {shrink} />
{/if}
{/if}
{:else if Ctor != null}
<ErrorBoundary bind:error>
@@ -0,0 +1,367 @@
<!--
// Copyright © 2025 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
-->
<script lang="ts">
export let size: 'small' | 'medium' | 'large' = 'medium'
</script>
<div
class="square-spinner-container {size === 'small' ? 'size-small' : size === 'medium' ? 'size-medium' : 'size-large'}"
>
<div class="loader"></div>
</div>
<style lang="scss">
.square-spinner-container {
display: inline-flex;
align-items: center;
justify-content: center;
width: 2.1875rem;
height: 2.1875rem;
}
.size-small {
width: 1.75rem;
height: 1.75rem;
}
.size-large {
width: 3.5rem;
height: 3.5rem;
}
.loader {
height: 0.3125rem;
width: 0.3125rem;
color: var(--theme-trans-color);
box-shadow:
-0.625rem -0.625rem 0 0.3125rem,
-0.625rem -0.625rem 0 0.3125rem,
-0.625rem -0.625rem 0 0.3125rem,
-0.625rem -0.625rem 0 0.3125rem;
animation: squareSpinner 6s infinite;
}
/* Size variations */
.size-small .loader {
height: 0.25rem;
width: 0.25rem;
box-shadow:
-0.5rem -0.5rem 0 0.25rem,
-0.5rem -0.5rem 0 0.25rem,
-0.5rem -0.5rem 0 0.25rem,
-0.5rem -0.5rem 0 0.25rem;
}
.size-large .loader {
height: 0.5rem;
width: 0.5rem;
box-shadow:
-1rem -1rem 0 0.5rem,
-1rem -1rem 0 0.5rem,
-1rem -1rem 0 0.5rem,
-1rem -1rem 0 0.5rem;
}
@keyframes squareSpinner {
0% {
box-shadow:
-0.625rem -0.625rem 0 0.3125rem,
-0.625rem -0.625rem 0 0.3125rem,
-0.625rem -0.625rem 0 0.3125rem,
-0.625rem -0.625rem 0 0.3125rem;
}
8.33% {
box-shadow:
-0.625rem -0.625rem 0 0.3125rem,
0.625rem -0.625rem 0 0.3125rem,
0.625rem -0.625rem 0 0.3125rem,
0.625rem -0.625rem 0 0.3125rem;
}
16.66% {
box-shadow:
-0.625rem -0.625rem 0 0.3125rem,
0.625rem -0.625rem 0 0.3125rem,
0.625rem 0.625rem 0 0.3125rem,
0.625rem 0.625rem 0 0.3125rem;
}
24.99% {
box-shadow:
-0.625rem -0.625rem 0 0.3125rem,
0.625rem -0.625rem 0 0.3125rem,
0.625rem 0.625rem 0 0.3125rem,
-0.625rem 0.625rem 0 0.3125rem;
}
33.32% {
box-shadow:
-0.625rem -0.625rem 0 0.3125rem,
0.625rem -0.625rem 0 0.3125rem,
0.625rem 0.625rem 0 0.3125rem,
-0.625rem -0.625rem 0 0.3125rem;
}
41.65% {
box-shadow:
0.625rem -0.625rem 0 0.3125rem,
0.625rem -0.625rem 0 0.3125rem,
0.625rem 0.625rem 0 0.3125rem,
0.625rem -0.625rem 0 0.3125rem;
}
49.98% {
box-shadow:
0.625rem 0.625rem 0 0.3125rem,
0.625rem 0.625rem 0 0.3125rem,
0.625rem 0.625rem 0 0.3125rem,
0.625rem 0.625rem 0 0.3125rem;
}
58.31% {
box-shadow:
-0.625rem 0.625rem 0 0.3125rem,
-0.625rem 0.625rem 0 0.3125rem,
0.625rem 0.625rem 0 0.3125rem,
-0.625rem 0.625rem 0 0.3125rem;
}
66.64% {
box-shadow:
-0.625rem -0.625rem 0 0.3125rem,
-0.625rem -0.625rem 0 0.3125rem,
0.625rem 0.625rem 0 0.3125rem,
-0.625rem 0.625rem 0 0.3125rem;
}
74.97% {
box-shadow:
-0.625rem -0.625rem 0 0.3125rem,
0.625rem -0.625rem 0 0.3125rem,
0.625rem 0.625rem 0 0.3125rem,
-0.625rem 0.625rem 0 0.3125rem;
}
83.3% {
box-shadow:
-0.625rem -0.625rem 0 0.3125rem,
0.625rem 0.625rem 0 0.3125rem,
0.625rem 0.625rem 0 0.3125rem,
-0.625rem 0.625rem 0 0.3125rem;
}
91.63% {
box-shadow:
-0.625rem -0.625rem 0 0.3125rem,
-0.625rem 0.625rem 0 0.3125rem,
-0.625rem 0.625rem 0 0.3125rem,
-0.625rem 0.625rem 0 0.3125rem;
}
100% {
box-shadow:
-0.625rem -0.625rem 0 0.3125rem,
-0.625rem -0.625rem 0 0.3125rem,
-0.625rem -0.625rem 0 0.3125rem,
-0.625rem -0.625rem 0 0.3125rem;
}
}
/* Size-specific animations */
.size-small .loader {
animation: squareSpinnerSmall 6s infinite;
}
.size-large .loader {
animation: squareSpinnerLarge 6s infinite;
}
@keyframes squareSpinnerSmall {
0% {
box-shadow:
-0.5rem -0.5rem 0 0.25rem,
-0.5rem -0.5rem 0 0.25rem,
-0.5rem -0.5rem 0 0.25rem,
-0.5rem -0.5rem 0 0.25rem;
}
8.33% {
box-shadow:
-0.5rem -0.5rem 0 0.25rem,
0.5rem -0.5rem 0 0.25rem,
0.5rem -0.5rem 0 0.25rem,
0.5rem -0.5rem 0 0.25rem;
}
16.66% {
box-shadow:
-0.5rem -0.5rem 0 0.25rem,
0.5rem -0.5rem 0 0.25rem,
0.5rem 0.5rem 0 0.25rem,
0.5rem 0.5rem 0 0.25rem;
}
24.99% {
box-shadow:
-0.5rem -0.5rem 0 0.25rem,
0.5rem -0.5rem 0 0.25rem,
0.5rem 0.5rem 0 0.25rem,
-0.5rem 0.5rem 0 0.25rem;
}
33.32% {
box-shadow:
-0.5rem -0.5rem 0 0.25rem,
0.5rem -0.5rem 0 0.25rem,
0.5rem 0.5rem 0 0.25rem,
-0.5rem -0.5rem 0 0.25rem;
}
41.65% {
box-shadow:
0.5rem -0.5rem 0 0.25rem,
0.5rem -0.5rem 0 0.25rem,
0.5rem 0.5rem 0 0.25rem,
0.5rem -0.5rem 0 0.25rem;
}
49.98% {
box-shadow:
0.5rem 0.5rem 0 0.25rem,
0.5rem 0.5rem 0 0.25rem,
0.5rem 0.5rem 0 0.25rem,
0.5rem 0.5rem 0 0.25rem;
}
58.31% {
box-shadow:
-0.5rem 0.5rem 0 0.25rem,
-0.5rem 0.5rem 0 0.25rem,
0.5rem 0.5rem 0 0.25rem,
-0.5rem 0.5rem 0 0.25rem;
}
66.64% {
box-shadow:
-0.5rem -0.5rem 0 0.25rem,
-0.5rem -0.5rem 0 0.25rem,
0.5rem 0.5rem 0 0.25rem,
-0.5rem 0.5rem 0 0.25rem;
}
74.97% {
box-shadow:
-0.5rem -0.5rem 0 0.25rem,
0.5rem -0.5rem 0 0.25rem,
0.5rem 0.5rem 0 0.25rem,
-0.5rem 0.5rem 0 0.25rem;
}
83.3% {
box-shadow:
-0.5rem -0.5rem 0 0.25rem,
0.5rem 0.5rem 0 0.25rem,
0.5rem 0.5rem 0 0.25rem,
-0.5rem 0.5rem 0 0.25rem;
}
91.63% {
box-shadow:
-0.5rem -0.5rem 0 0.25rem,
-0.5rem 0.5rem 0 0.25rem,
-0.5rem 0.5rem 0 0.25rem,
-0.5rem 0.5rem 0 0.25rem;
}
100% {
box-shadow:
-0.5rem -0.5rem 0 0.25rem,
-0.5rem -0.5rem 0 0.25rem,
-0.5rem -0.5rem 0 0.25rem,
-0.5rem -0.5rem 0 0.25rem;
}
}
@keyframes squareSpinnerLarge {
0% {
box-shadow:
-1rem -1rem 0 0.5rem,
-1rem -1rem 0 0.5rem,
-1rem -1rem 0 0.5rem,
-1rem -1rem 0 0.5rem;
}
8.33% {
box-shadow:
-1rem -1rem 0 0.5rem,
1rem -1rem 0 0.5rem,
1rem -1rem 0 0.5rem,
1rem -1rem 0 0.5rem;
}
16.66% {
box-shadow:
-1rem -1rem 0 0.5rem,
1rem -1rem 0 0.5rem,
1rem 1rem 0 0.5rem,
1rem 1rem 0 0.5rem;
}
24.99% {
box-shadow:
-1rem -1rem 0 0.5rem,
1rem -1rem 0 0.5rem,
1rem 1rem 0 0.5rem,
-1rem 1rem 0 0.5rem;
}
33.32% {
box-shadow:
-1rem -1rem 0 0.5rem,
1rem -1rem 0 0.5rem,
1rem 1rem 0 0.5rem,
-1rem -1rem 0 0.5rem;
}
41.65% {
box-shadow:
1rem -1rem 0 0.5rem,
1rem -1rem 0 0.5rem,
1rem 1rem 0 0.5rem,
1rem -1rem 0 0.5rem;
}
49.98% {
box-shadow:
1rem 1rem 0 0.5rem,
1rem 1rem 0 0.5rem,
1rem 1rem 0 0.5rem,
1rem 1rem 0 0.5rem;
}
58.31% {
box-shadow:
-1rem 1rem 0 0.5rem,
-1rem 1rem 0 0.5rem,
1rem 1rem 0 0.5rem,
-1rem 1rem 0 0.5rem;
}
66.64% {
box-shadow:
-1rem -1rem 0 0.5rem,
-1rem -1rem 0 0.5rem,
1rem 1rem 0 0.5rem,
-1rem 1rem 0 0.5rem;
}
74.97% {
box-shadow:
-1rem -1rem 0 0.5rem,
1rem -1rem 0 0.5rem,
1rem 1rem 0 0.5rem,
-1rem 1rem 0 0.5rem;
}
83.3% {
box-shadow:
-1rem -1rem 0 0.5rem,
1rem 1rem 0 0.5rem,
1rem 1rem 0 0.5rem,
-1rem 1rem 0 0.5rem;
}
91.63% {
box-shadow:
-1rem -1rem 0 0.5rem,
-1rem 1rem 0 0.5rem,
-1rem 1rem 0 0.5rem,
-1rem 1rem 0 0.5rem;
}
100% {
box-shadow:
-1rem -1rem 0 0.5rem,
-1rem -1rem 0 0.5rem,
-1rem -1rem 0 0.5rem,
-1rem -1rem 0 0.5rem;
}
}
</style>
@@ -299,7 +299,7 @@
</div>
<div class="app">
{#if application}
<Component is={application} props={{}} />
<Component is={application} appLoading={true} props={{}} />
{:else}
<div class="error">
Application not found: {application}
+1
View File
@@ -288,6 +288,7 @@ export { default as EmbeddedPDF } from './components/EmbeddedPDF.svelte'
export { default as NestedMenu } from './components/NestedMenu.svelte'
export { default as NestedDropdown } from './components/NestedDropdown.svelte'
export { default as StatusBarButton } from './components/StatusBarButton.svelte'
export { default as AppLoading } from './components/AppLoading.svelte'
export { default as Blurhash } from './components/Blurhash.svelte'
export { default as Dock } from './components/Dock.svelte'
@@ -18,8 +18,8 @@
import {
Button,
Component,
AppLoading,
Label,
Loading,
Notifications,
deviceOptionsStore,
fetchMetadataLocalStorage,
@@ -56,7 +56,7 @@
{:else}
{#key $location.path[1]}
{#await connect(getMetadata(workbenchRes.metadata.PlatformTitle) ?? 'Platform')}
<Loading>
<AppLoading>
{#if ($workspaceCreating ?? -1) >= 0}
<div class="ml-1">
<Label label={workbenchRes.string.WorkspaceCreating} />
@@ -73,7 +73,7 @@
<Label label={workbench.string.UpgradeDownloadProgress} params={{ percent: $upgradeDownloadProgress }} />
</div>
{/if}
</Loading>
</AppLoading>
{:then client}
{#if $versionError}
<div class="version-wrapper">