Skip to content

Commit

Permalink
Add skeleton/loading components
Browse files Browse the repository at this point in the history
  • Loading branch information
rendall committed Aug 18, 2023
1 parent d119bf0 commit 9af068a
Show file tree
Hide file tree
Showing 11 changed files with 376 additions and 66 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ yarn-error.log
stats.json
globalConfig.json
debug.log
/scripts/cypress/
/node_modules
/lib
/functions
Expand All @@ -18,6 +19,6 @@ debug.log
.env
*/*.js
*.map
!/dist/css/simple-comment-style.css
!/docs/**/*.js
!/dist/css/simple-comment-style.css
!/cypress/**/*.js
1 change: 1 addition & 0 deletions cypress.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export default defineConfig({
},
baseUrl: "http://localhost:7070",
excludeSpecPattern: "**/examples/*.spec.js",
experimentalRunAllSpecs: true,
},

component: {
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"lint": "eslint .",
"posttest": "rimraf ./globalConfig.json",
"prebuild": "rimraf ./functions && rimraf ./dist/js && mkdir -p ./dist/js",
"start:db": "sudo systemctl start mongod",
"start:backend": "netlify dev --port 7070",
"start:frontend": "webpack-dev-server --config ./webpack.frontend.js",
"start": "NODE_ENV=\"development\" NODE_OPTIONS=\"--max-old-space-size=4096\" netlify dev --port 7070 --command \"yarn run start:frontend\"",
Expand Down
13 changes: 10 additions & 3 deletions src/components/CommentDisplay.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,17 @@
>
{#if comment.dateDeleted}
<header class="comment-header">
<div class="user-avatar" />
<div class="user-avatar">
<div class="gray-block" />
</div>
<div class="comment-info">
<p class="user-name" />
<p class="comment-date" />
<div class="user-name">
<div class="gray-block" />
<div class="gray-block" />
</div>
<div class="comment-date">
<div class="gray-block" />
</div>
</div>
</header>
<article class="comment-body">
Expand Down
43 changes: 35 additions & 8 deletions src/components/CommentInput.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
ServerResponse,
User,
} from "../lib/simple-comment-types"
import Login from "./Login.svelte"
import SkeletonCommentInput from "./low-level/SkeletonCommentInput.svelte"
import { StateValue } from "xstate"
import { commentPostMachine } from "../lib/commentPost.xstate"
import { createEventDispatcher, onDestroy } from "svelte"
import { isResponseOk } from "../frontend-utilities"
import { createEventDispatcher, onDestroy, onMount } from "svelte"
import { dispatchableStore, loginStateStore } from "../lib/svelte-stores"
import { isResponseOk } from "../frontend-utilities"
import { postComment } from "../apiClient"
import { useMachine } from "@xstate/svelte"
import Login from "./Login.svelte"
export let currentUser: User | undefined
export let commentId: CommentId
export let onCancel = null
Expand All @@ -20,6 +22,9 @@
let commentText = ""
let loginStateValue
let textareaRef
let textAreaWidth = "100%"
let textAreaHeight = "7rem"
const { state, send } = useMachine(commentPostMachine)
const dispatch = createEventDispatcher()
Expand Down Expand Up @@ -119,6 +124,20 @@
send({ type: "RESET" })
}
const resizeObserver = new ResizeObserver(([textArea]) => {
if (isProcessing) return
const { inlineSize, blockSize } = textArea.borderBoxSize[0] ?? {
inlineSize: "100%",
blockSize: "7rem",
}
textAreaWidth = `${inlineSize}px`
textAreaHeight = `${blockSize}px`
})
onMount(() => {
resizeObserver.observe(textareaRef)
})
onDestroy(() => {
unsubscribeLoginState()
})
Expand All @@ -137,20 +156,28 @@
if ($state.value === stateValue) stateHandler()
})
}
$: isProcessing = (
["validating", "loggingIn", "posting", "deleting"] as StateValue[]
).includes($state.value)
</script>

<form class="comment-form" on:submit={onSubmit}>
<SkeletonCommentInput
width={textAreaWidth}
height={textAreaHeight}
isHidden={!isProcessing}
/>
<form class="comment-form" class:is-hidden={isProcessing} on:submit={onSubmit}>
<!-- svelte-ignore a11y-autofocus -->
<textarea
class="comment-field"
{placeholder}
bind:this={textareaRef}
bind:value={commentText}
required
{autofocus}
bind:value={commentText}
{placeholder}
/>
<!-- {#if !currentUser} -->
<Login {currentUser} />
<!-- {/if} -->
<div class="button-row">
{#if onCancel !== null}
<button class="comment-cancel-button" on:click={onCancel}>Cancel</button>
Expand Down
10 changes: 8 additions & 2 deletions src/components/DiscussionDisplay.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import { discussionMachine } from "../lib/discussion.xstate"
import { isResponseOk, threadComments } from "../frontend-utilities"
import CommentInput from "./CommentInput.svelte"
import SkeletonComment from "./low-level/SkeletonComment.svelte"
export let discussionId: string
export let title: string
Expand Down Expand Up @@ -115,7 +116,7 @@
updateStatusDisplay("loaded")
}
// Update the single source of truth
// Update the single source of truth without roundtrip to the server
const onCommentPosted = commentPostedEvent => {
const { comment } = commentPostedEvent.detail
repliesFlatArray = [{ ...comment, new: true }, ...repliesFlatArray]
Expand All @@ -131,7 +132,8 @@
event.preventDefault()
showReply = discussionId
}
// Update the single source of truth
// Update the single source of truth without roundtrip to the server
const onCommentDeleted = commentDeletedEvent => {
const { commentId } = commentDeletedEvent.detail
const comment = repliesFlatArray.find(comment => comment.id === commentId)
Expand Down Expand Up @@ -198,4 +200,8 @@
on:posted={onCommentPosted}
/>
{/if}
{#if $state.value === "loading"}
<SkeletonComment />
<div style="margin-bottom:10rem" />
{/if}
</section>
83 changes: 58 additions & 25 deletions src/components/SelfDisplay.svelte
Original file line number Diff line number Diff line change
@@ -1,44 +1,77 @@
<script lang="ts">
import { idIconDataUrl } from "../frontend-utilities"
import SkeletonText from "./low-level/SkeletonText.svelte"
import type { User } from "../lib/simple-comment-types"
import { dispatchableStore, loginStateStore } from "../lib/svelte-stores"
import { fade } from "svelte/transition"
import { idIconDataUrl } from "../frontend-utilities"
export let currentUser: User | undefined = undefined
export let currentUser: User | undefined
let loginStateValue
let loginStateNextEvents
let isProcessing = true
loginStateStore.subscribe(state => {
const { value, nextEvents } = state
loginStateValue = value
loginStateNextEvents = nextEvents
})
$: {
isProcessing =
loginStateValue === "verifying" ||
loginStateValue === "loggingIn" ||
loginStateValue === "loggingOut"
}
const onLogoutClick = (e: Event) => {
e.preventDefault()
dispatchableStore.dispatch("logoutIntent")
}
</script>

{#if loginStateValue === "verifying" || loginStateValue === "loggingIn" || loginStateValue === "loggingOut"}
<section class="self-display">
<div class="self-avatar skeleton" />
<div class="self-info skeleton" />
</section>
{:else if currentUser}
<section class="self-display" id="self-display">
<div class="self-avatar">
<img src={idIconDataUrl(currentUser.id)} alt="" />
</div>
<div class="self-info">
<h2 id="self-user-name">{currentUser.name}</h2>
<p id="self-name">
@{currentUser.id}
{currentUser.isAdmin ? "(admin)" : ""}
</p>
<p id="self-email">{currentUser.email}</p>
</div>
{#if loginStateNextEvents.includes("LOGOUT")}
<button id="log-out-button" on:click={onLogoutClick}>Log out</button>
{/if}
</section>
{/if}
<div class="self-display-container">
{#if isProcessing}
<section class="skeleton self-display" transition:fade>
<div class="self-avatar skeleton"><SkeletonText avatar /></div>
<div class="self-info">
<h2><SkeletonText width="30%" height="1.2rem" /></h2>
<p><SkeletonText width="50%" /></p>
<p><SkeletonText width="45%" /></p>
</div>
<div><SkeletonText width="7rem" height="2rem" /></div>
</section>
{/if}

{#if currentUser}
<section class="self-display" id="self-display">
<div class="self-avatar">
<img src={idIconDataUrl(currentUser.id)} alt="" />
</div>
<div class="self-info">
<h2 id="self-user-name">{currentUser.name}</h2>
<p id="self-name">
@{currentUser.id}
{currentUser.isAdmin ? "(admin)" : ""}
</p>
<p id="self-email">{currentUser.email}</p>
</div>
{#if loginStateNextEvents.includes("LOGOUT")}
<button id="log-out-button" on:click={onLogoutClick}>Log out</button>
{/if}
</section>
{/if}
</div>

<style lang="scss">
.self-display-container {
position: relative;
width: 100%;
display: block;
height: 10rem;
.self-display.skeleton {
position: absolute;
width: 100%;
}
}
</style>
57 changes: 57 additions & 0 deletions src/components/low-level/SkeletonComment.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<script lang="ts">
import SkeletonText from "./SkeletonText.svelte"
export let isHidden = false
</script>

<div class="skeleton-comment" class:is-hidden={isHidden}>
<header class="comment-header">
<div class="user-avatar"><SkeletonText avatar /></div>
<div class="comment-info">
<p class="user-name"><SkeletonText width="8rem" /></p>
<p class="comment-date"><SkeletonText width="12rem" /></p>
</div>
</header>
<article class="comment-body">
<SkeletonText paragraph />
</article>
</div>

<style lang="scss">
.comment-header {
align-items: center;
display: flex;
flex-direction: row;
margin-bottom: 0.25rem;
min-height: unset;
p {
margin: 0;
padding: 0;
}
.user-avatar {
flex: 0 0 3rem;
margin-right: 0.9rem;
display: inline-flex;
max-width: 2.5rem;
border-radius: 50%;
overflow: hidden;
}
.comment-info {
display: flex;
flex-direction: row;
justify-content: space-between;
width: calc(100% - 3rem - 1.3rem);
.user-name {
margin-right: 1rem;
}
}
}
.comment-body {
margin-left: 0.5rem;
padding-left: 2rem;
}
</style>
11 changes: 11 additions & 0 deletions src/components/low-level/SkeletonCommentInput.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<script lang="ts">
import SkeletonText from "./SkeletonText.svelte"
export let isHidden = false
export let width = "100%"
export let height = "7rem"
</script>

<div class="skeleton-comment-input" class:is-hidden={isHidden}>
<SkeletonText {width} {height} />
<div class="button-row"><SkeletonText width="10rem" height="2rem" /></div>
</div>
Loading

0 comments on commit 9af068a

Please sign in to comment.