소스 검색

Add loading spin & minor style adjustments

jerryliao 1 년 전
부모
커밋
4a749900c8
8개의 변경된 파일111개의 추가작업 그리고 40개의 파일을 삭제
  1. 2 1
      import_map.json
  2. 1 1
      islands/Editor.tsx
  3. 19 4
      islands/LoginFrame.tsx
  4. 1 1
      routes/api/post.tsx
  5. 1 1
      routes/api/user/login.tsx
  6. 62 4
      static/global.css
  7. 0 28
      utils/server.ts
  8. 25 0
      utils/ui.ts

+ 2 - 1
import_map.json

@@ -5,6 +5,7 @@
     "preact": "https://esm.sh/preact@10.8.2",
     "preact/": "https://esm.sh/preact@10.8.2/",
     "preact-render-to-string": "https://esm.sh/preact-render-to-string@5.2.0?deps=preact@10.8.2",
-    "showdown": "https://esm.sh/showdown@2.1.0"
+    "showdown": "https://esm.sh/showdown@2.1.0",
+    "utils/": "./utils/"
   }
 }

+ 1 - 1
islands/Editor.tsx

@@ -2,7 +2,7 @@
 import { h, render } from "preact";
 import { useEffect, useState, useRef } from "preact/hooks";
 import showdown, { Converter } from "showdown";
-import { showLoading, hideLoading } from "../utils.ts";
+import { showLoading, hideLoading } from "utils/ui.ts";
 
 interface EditorProps {
   id: string;

+ 19 - 4
islands/LoginFrame.tsx

@@ -1,10 +1,13 @@
 /** @jsx h */
 import { h } from "preact";
 import { useState, useEffect } from "preact/hooks";
+import { showLoading, hideLoading } from "utils/ui.ts";
 
 export default function LoginFrame() {
   const [email, setEmail] = useState("");
   const [password, setPassword] = useState("");
+  const [emailError, setEmailError] = useState(false);
+  const [passwordError, setPasswordError] = useState(false);
 
   const checkUserLogin = async () => {
     const resp = await fetch("/api/user/login");
@@ -38,31 +41,43 @@ export default function LoginFrame() {
     checkUserLogin();
   }, []);
 
-  const onSubmit = () => {
+  const onSubmit = async () => {
+    showLoading();
     if (email && password) {
-      doUserLogin();
+      await doUserLogin();
     }
+
+    // Set error
+    if (!email) {
+      setEmailError(true);
+    }
+    if (!password) {
+      setPasswordError(true);
+    }
+    hideLoading();
   };
 
   return (
     <div className="pd-login-frame">
       <span className="pd-login-input-label">Email</span>
       <input
-        className="pd-login-input"
+        className={`pd-login-input${emailError ? " error" : ""}`}
         type="text"
         placeholder="Your email"
         value={email}
         onInput={(e) => {
+          setEmailError(false);
           setEmail((e.target as HTMLInputElement).value);
         }}
       />
       <span className="pd-login-input-label">Password</span>
       <input
-        className="pd-login-input"
+        className={`pd-login-input${passwordError ? " error" : ""}`}
         type="password"
         placeholder="Your password"
         value={password}
         onInput={(e) => {
+          setPasswordError(false);
           setPassword((e.target as HTMLInputElement).value);
         }}
       />

+ 1 - 1
routes/api/post.tsx

@@ -3,7 +3,7 @@ import {
   checkToken,
   makeErrorResponse,
   makeSuccessResponse,
-} from "../../utils.ts";
+} from "utils/server.ts";
 
 export const handler: Handlers = {
   async GET(req: Request) {

+ 1 - 1
routes/api/user/login.tsx

@@ -4,7 +4,7 @@ import {
   makeErrorResponse,
   makeSuccessResponse,
   setToken,
-} from "../../../utils.ts";
+} from "utils/server.ts";
 
 export const handler: Handlers = {
   GET(req: Request) {

+ 62 - 4
static/global.css

@@ -26,7 +26,7 @@ textarea {
   box-sizing: border-box;
   border-radius: 0.375rem;
   border: 1px solid #ced4da;
-  font-size: 1rem;
+  font-size: 14px;
   outline: none;
 }
 
@@ -49,12 +49,60 @@ button {
   line-height: 16px;
   color: #212529;
   cursor: pointer;
-  font-size: 1rem;
+  font-size: 14px;
   border: 1px solid #ced4da;
   border-radius: 0.375rem;
 }
 /* Global form styles end */
 
+/* Loading styles start */
+/* Loading spin from https://loading.io/css/ */
+.pd-loading-cover {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background-color: rgba(0, 0, 0, 0.6);
+  z-index: 9;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.pd-loading-cover .pd-loading-spin {
+  display: inline-block;
+  transform: translateZ(1px);
+}
+
+.pd-loading-cover .pd-loading-spin .pd-loading-spin-inner {
+  display: inline-block;
+  width: 64px;
+  height: 64px;
+  margin: 8px;
+  border-radius: 50%;
+  background: #fff;
+  animation: pd-loading-spin 5s cubic-bezier(0, 0.2, 0.8, 1) infinite;
+}
+
+@keyframes pd-loading-spin {
+  0%,
+  100% {
+    animation-timing-function: cubic-bezier(0.5, 0, 1, 0.5);
+  }
+  0% {
+    transform: rotateY(0deg);
+  }
+  50% {
+    transform: rotateY(1800deg);
+    animation-timing-function: cubic-bezier(0, 0.5, 0.5, 1);
+  }
+  100% {
+    transform: rotateY(3600deg);
+  }
+}
+/* Loading styles end */
+
 /* Login frame styles start */
 .pd-login-frame {
   width: 375px;
@@ -63,7 +111,6 @@ button {
   border-radius: 0.375rem;
   box-sizing: border-box;
   padding: 16px;
-  font-size: 1rem;
   color: #212529;
   display: flex;
   flex-direction: column;
@@ -73,8 +120,13 @@ button {
   margin-bottom: 8px;
 }
 
+.pd-login-frame .pd-login-input.error {
+  border-color: #dc3545;
+}
+
 .pd-login-frame .pd-login-input-label {
   margin-bottom: 4px;
+  font-size: 14px;
 }
 
 .pd-login-frame .pd-login-btn {
@@ -97,7 +149,7 @@ button {
   border: 1px solid #ced4da;
   border-radius: 0.375rem;
   box-sizing: border-box;
-  font-size: 1rem;
+  font-size: 14px;
 }
 
 .pd-top-bar .pd-top-bar-mode-switcher .pd-top-bar-btn {
@@ -146,6 +198,12 @@ button {
   margin-right: 16px;
   font-size: 16px;
   cursor: pointer;
+  height: 16px;
+  width: 16px;
+}
+
+.pd-top-bar .pd-top-bar-tool-icons i.bi::before {
+  line-height: unset;
 }
 
 .pd-top-bar .pd-top-bar-tool-icons i.bi:hover {

+ 0 - 28
utils.ts → utils/server.ts

@@ -2,7 +2,6 @@ import { setCookie, getCookies, deleteCookie } from "$http/cookie.ts";
 
 export function checkToken(req: Request) {
   const cookies = getCookies(req.headers);
-  console.log("DIOR::", cookies);
   if (cookies && cookies["pd-user-token"]) {
     return true;
   }
@@ -45,30 +44,3 @@ export function makeErrorResponse() {
     }
   );
 }
-
-export function showLoading() {
-  if (document && document.body) {
-    const coverEle = document.body.querySelector(".pd-cover");
-    if (!coverEle) {
-      const newCoverEle = document.createElement("div");
-      newCoverEle.className = "pd-cover";
-      newCoverEle.style.position = "fixed";
-      newCoverEle.style.top = "0";
-      newCoverEle.style.left = "0";
-      newCoverEle.style.right = "0";
-      newCoverEle.style.bottom = "0";
-      newCoverEle.style.backgroundColor = "rgba(0, 0, 0, 0.8)";
-      newCoverEle.style.zIndex = "9";
-      document.body.appendChild(newCoverEle);
-    }
-  }
-}
-
-export function hideLoading() {
-  if (document && document.body) {
-    const coverEle = document.body.querySelector(".pd-cover");
-    if (coverEle) {
-      document.body.removeChild(coverEle);
-    }
-  }
-}

+ 25 - 0
utils/ui.ts

@@ -0,0 +1,25 @@
+export function showLoading() {
+  if (document && document.body) {
+    const coverEle = document.body.querySelector(".pd-loading-cover");
+    if (!coverEle) {
+      const newCoverEle = document.createElement("div");
+      const newCoverSpinEle = document.createElement("div");
+      const newCoverSpinInnerEle = document.createElement("div");
+      newCoverEle.className = "pd-loading-cover";
+      newCoverSpinEle.className = "pd-loading-spin";
+      newCoverSpinInnerEle.className = "pd-loading-spin-inner";
+      newCoverSpinEle.appendChild(newCoverSpinInnerEle);
+      newCoverEle.appendChild(newCoverSpinEle);
+      document.body.appendChild(newCoverEle);
+    }
+  }
+}
+
+export function hideLoading() {
+  if (document && document.body) {
+    const coverEle = document.body.querySelector(".pd-loading-cover");
+    if (coverEle) {
+      document.body.removeChild(coverEle);
+    }
+  }
+}