完成基本的几个页面样式

main
落雨楓 2 years ago
parent fb48b7d2c5
commit 56054da74a

@ -8,7 +8,7 @@
<link rel="stylesheet" href="./src/style.scss">
</head>
<body>
<body class="bg-light">
<div id="app"></div>
<script type="module" src="./src/main.ts" type="text/javascript"></script>
</body>

@ -20,6 +20,7 @@
},
"dependencies": {
"bootstrap-icons": "^1.10.2",
"solid-js": "^1.6.5"
"solid-js": "^1.6.5",
"solid-transition-group": "^0.0.12"
}
}

@ -1,8 +1,49 @@
import { createSignal } from 'solid-js'
import { createEffect, createSignal, onMount } from 'solid-js'
import { render } from 'solid-js/web'
import { Transition } from 'solid-transition-group'
import { ConnectingWell } from './components/ConnectingWell'
import { HeightTransition } from './components/HeightTransition'
import { PairWell } from './components/PairWell'
import { PlayerActivationWell } from './components/PlayerActivationWell'
import { PlayerWell } from './components/PlayerWell'
import { ServerStatus, ServerStatusType } from './components/ServerStatus'
export const App = () => {
const [isConnected, setIsConnected] = createSignal(false)
const [enabled, setEnabled] = createSignal(false)
const [firstConnected, setFirstConneted] = createSignal(false)
const [serverStatus, setServerStatus] = createSignal<ServerStatusType>('connecting')
const [boundUser, setBoundUser] = createSignal<any>()
createEffect(() => {
if (serverStatus() === 'connected') {
setFirstConneted(true)
}
}, serverStatus())
onMount(() => {
setTimeout(() => {
setServerStatus('connected')
}, 3000)
})
const renderWell = () => {
if (!enabled() || !firstConnected()) {
return (
<PlayerActivationWell onEnabled={() => setEnabled(true)} connecting={!firstConnected()} />
)
} else {
if (!boundUser()) {
return (
<PairWell />
)
} else {
return (
<PlayerWell />
)
}
}
}
return (
<>
@ -17,23 +58,15 @@ export const App = () => {
</div>
</nav>
<div class="container py-5">
<div class="p-5 mb-4 bg-light shadow rounded-3">
{!isConnected() ? (
<div class="container-fluid py-4">
<p class="text-success fs-5"><i class="bi bi-check-circle"></i> </p>
<h1 class="display-5 fw-bold"></h1>
<p class="col-md-8 fs-4"></p>
<div class="card bg-dark p-2">
<code class="text-light">/ipm bind 123456</code>
</div>
</div>
) : (
<div class="container-fluid py-5">
<h1 class="display-5 fw-bold"></h1>
<p class="col-md-8 fs-4"></p>
<button class="btn btn-primary btn-lg" type="button"></button>
</div>
)}
<div class="p-5 mb-4 bg-white shadow rounded-3">
<div class="container-fluid py-4">
<p class="fs-5">
<ServerStatus type={serverStatus()} />
</p>
<HeightTransition>
{renderWell()}
</HeightTransition>
</div>
</div>
</div>
</>

@ -1,37 +0,0 @@
import { createEffect, createSignal, JSXElement, onCleanup, onMount, useTransition } from "solid-js"
export type CardProps = {
title?: string | JSXElement,
children?: any
}
export const Card = (props: CardProps) => {
const [outerHeight, setOuterHeight] = createSignal<number>(0)
let innerElem: HTMLDivElement | undefined
onMount(() => {
if (innerElem) {
setOuterHeight(innerElem?.offsetHeight)
}
})
createEffect(() => {
if (innerElem) {
setOuterHeight(innerElem?.offsetHeight)
}
})
return (
<div class="p-5 mb-4 bg-light rounded-3">
<div class="container-fluid py-5">
<div class="card-wrapper" style={{ height: outerHeight() + 'px' }}>
<div class="card-inner" ref={innerElem}>
<h1 class="display-5 fw-bold">{props.title}</h1>
<p class="col-md-8 fs-4"></p>
<button class="btn btn-primary btn-lg" type="button">Example button</button>
</div>
</div>
</div>
</div>
)
}

@ -0,0 +1,8 @@
export const ConnectingWell = () => {
return (
<div>
<h1 class="display-5 fw-bold"></h1>
<p class="col-md-8 fs-4"></p>
</div>
)
}

@ -0,0 +1,11 @@
.height-transition-container {
overflow: hidden;
position: relative;
transition: height 350ms ease-in-out;
height: auto;
}
.height-transition-inner {
width: 100%;
height: auto;
}

@ -0,0 +1,31 @@
import { createSignal, JSXElement } from "solid-js"
import { Transition } from "solid-transition-group"
import "./index.scss";
export type HeightTransitionProps = {
children: JSXElement
}
export const HeightTransition = (props: HeightTransitionProps) => {
const [outerHeight, setOuterHeight] = createSignal<number | undefined>()
return (
<div class="height-transition-container" style={{ height: outerHeight() ? outerHeight() + 'px' : undefined }}>
<div class="height-transition-inner">
<Transition
name="fade-left"
onEnter={(el, done) => {
setOuterHeight((el as any).offsetHeight);
setTimeout(done, 400);
}}
onExit={(el, done) => {
setOuterHeight((el as any).offsetHeight);
setTimeout(done, 400);
}}
>
{props.children}
</Transition>
</div>
</div>
)
}

@ -0,0 +1,11 @@
export const PairWell = () => {
return (
<div>
<h1 class="display-5 fw-bold"></h1>
<p class="col-md-8 fs-4"></p>
<div class="card bg-dark p-2">
<code class="text-light">/ipm bind 123456</code>
</div>
</div>
)
}

@ -0,0 +1,41 @@
import { createSignal } from "solid-js"
import { SuccessAnimateIcon } from "./SuccessAnimateIcon"
export type PlayerActivationWellProps = {
connecting?: boolean,
onEnabled?: () => any
}
export const PlayerActivationWell = (props: PlayerActivationWellProps) => {
const [processing, setProcessing] = createSignal(false)
const [enabled, setEnabled] = createSignal(false)
const [error, setError] = createSignal<string|undefined>()
const handleEnableClick = () => {
setProcessing(true)
setEnabled(true)
// 等待成功动画结束
setTimeout(() => {
props.onEnabled?.()
}, 1500)
}
return (
<div>
<h1 class="display-5 fw-bold"></h1>
<p class="col-md-8 fs-4"></p>
<div class="d-flex gap-4 align-items-center">
<button class="btn btn-primary btn-lg" type="button" disabled={processing() || enabled()} onClick={handleEnableClick}></button>
{enabled() && (
<SuccessAnimateIcon />
)}
{!error() && enabled() && props.connecting && (
<span class="fs-5 text-secondary"></span>
)}
{error() && (
<span class="fs-6 text-danger">: {error()}</span>
)}
</div>
</div>
)
}

@ -0,0 +1,8 @@
export const PlayerWell = () => {
return (
<div>
<h1 class="display-5 fw-bold"></h1>
<p class="col-md-8 fs-4">: </p>
</div>
)
}

@ -0,0 +1,36 @@
import { Match, mergeProps, Switch } from "solid-js";
import { Transition } from "solid-transition-group"
export type ServerStatusType = 'connecting' | 'connected' | 'error'
export type ServerStatusProps = {
type?: ServerStatusType,
error?: string
}
export const ServerStatus = (srcProps: ServerStatusProps) => {
const props = mergeProps({
type: 'connecting',
}, srcProps)
return (
<div class="anim-scroll-container">
<Transition name="slide-up">
<Switch>
<Match when={props.type === "connecting"}>
<div class="text-primary d-flex align-items-center">
<div class="spinner-grow spinner-grow-sm me-2" role="status"></div>
<span></span>
</div>
</Match>
<Match when={props.type === "connected"}>
<div class="text-success"><i class="bi bi-check-circle"></i> </div>
</Match>
<Match when={props.type === "error"}>
<div class="text-danger"> {props.error && (': ' + props.error)}</div>
</Match>
</Switch>
</Transition>
</div>
)
}

@ -0,0 +1,45 @@
.success-animation {
display: inline-block;
padding: 6px;
.checkmark {
width: 36px;
height: 36px;
border-radius: 50%;
--bs-bg-opacity: 1;
stroke-width: 3px;
stroke: rgb(var(--bs-success-rgb));
stroke-miterlimit: 10;
box-shadow: inset 0px 0px 0px rgb(var(--bs-success-rgb));
animation: successAnimIconFill .4s ease-in-out .4s forwards;
&__circle {
stroke-dasharray: 166;
stroke-dashoffset: 166;
stroke-width: 3px;
stroke-miterlimit: 10;
stroke: rgb(var(--bs-success-rgb));
fill: rgb(var(--bs-white-rgb));
animation: successAnimIconStroke 0.6s cubic-bezier(0.65, 0, 0.45, 1) forwards;
}
&__check {
transform-origin: 50% 50%;
stroke-dasharray: 48;
stroke-dashoffset: 48;
animation: successAnimIconStroke 0.3s cubic-bezier(0.65, 0, 0.45, 1) 0.6s forwards;
}
}
}
@keyframes successAnimIconStroke {
100% {
stroke-dashoffset: 0;
}
}
@keyframes successAnimIconFill {
100% {
box-shadow: inset 0px 0px 0px 30px #4bb71b;
}
}

@ -0,0 +1,12 @@
import "./index.scss"
export const SuccessAnimateIcon = () => {
return (
<div class="success-animation">
<svg class="checkmark" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 52 52">
<circle class="checkmark__circle" cx="26" cy="26" r="25" fill="none" />
<path class="checkmark__check" fill="none" d="M14.1 27.2l7.1 7.2 16.7-16.8" />
</svg>
</div>
)
};

@ -17,31 +17,31 @@
@import "images";
@import "containers";
@import "grid";
@import "tables";
// @import "tables";
@import "forms";
@import "buttons";
@import "transitions";
// @import "transitions";
@import "dropdown";
@import "button-group";
@import "nav";
@import "navbar";
@import "card";
@import "accordion";
@import "breadcrumb";
@import "pagination";
@import "badge";
@import "alert";
@import "progress";
@import "list-group";
// @import "accordion";
// @import "breadcrumb";
// @import "pagination";
// @import "badge";
// @import "alert";
// @import "progress";
// @import "list-group";
@import "close";
@import "toasts";
// @import "toasts";
@import "modal";
@import "tooltip";
@import "popover";
@import "carousel";
// @import "tooltip";
// @import "popover";
// @import "carousel";
@import "spinners";
@import "offcanvas";
@import "placeholders";
// @import "offcanvas";
// @import "placeholders";
// Helpers
@import "helpers";

@ -1,2 +1,4 @@
@import "./libs/bootstrap/scss/bootstrap.scss";
@import "bootstrap-icons/font/bootstrap-icons.css";
@import "bootstrap-icons/font/bootstrap-icons.css";
@import "./styles/animate.scss";

@ -0,0 +1,94 @@
.anim-scroll-container {
overflow-y: hidden;
position: relative;
}
.slide-up {
&-enter-active {
transform: translate3d(0, 100%, 0);
transition: transform 350ms ease-in-out;
}
&-enter-to {
transform: translate3d(0, 0, 0);
}
&-exit-active {
position: absolute;
top: 0;
left: 0;
width: 100%;
transform: translate3d(0, 0, 0);
transition: transform 350ms ease-in-out;
}
&-exit-to {
transform: translate3d(0, -100%, 0);
}
}
.fade {
&-enter {
opacity: 0;
}
&-enter-active {
display: inherit;
opacity: 0;
transition: opacity 150ms linear;
}
&-enter-to {
opacity: 1;
}
&-exit {
opacity: 1;
}
&-exit-active {
opacity: 1;
transition: opacity 150ms linear;
}
&-exit-to {
opacity: 0;
}
}
.fade-left {
&-enter {
opacity: 0;
}
&-enter-active {
display: inherit;
opacity: 0;
transform: translate3d(5%, 0, 0);
transition: opacity 250ms linear, transform 250ms ease-in-out;
}
&-enter-to {
opacity: 1;
transform: translate3d(0, 0, 0);
}
&-exit {
opacity: 1;
}
&-exit-active {
position: absolute;
top: 0;
left: 0;
width: 100%;
opacity: 1;
transform: translate3d(0, 0, 0);
transition: opacity 250ms linear, transform 250ms ease-in-out;
}
&-exit-to {
opacity: 0;
transform: translate3d(-5%, 0, 0);
}
}

@ -5,6 +5,7 @@
"moduleResolution": "node",
"strict": true,
"jsx": "preserve",
"jsxImportSource": "solid-js",
"sourceMap": true,
"resolveJsonModule": true,
"esModuleInterop": true,

Loading…
Cancel
Save