mirror of
https://github.com/stack-auth/stack.git
synced 2026-06-13 21:01:21 +08:00
added loading indicator, fixed loading buttons
This commit is contained in:
parent
5dcc831f96
commit
5ae06585f2
@ -66,6 +66,21 @@ export default function PageClient() {
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', gap: 5 }}>
|
||||
<Button size='md' variant="primary" loading>
|
||||
Button
|
||||
</Button>
|
||||
<Button size='md' variant="secondary" loading>
|
||||
Button
|
||||
</Button>
|
||||
<Button size='md' variant="warning" loading>
|
||||
Button
|
||||
</Button>
|
||||
<Button size='md' color='orange' loading>
|
||||
Button
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<div style={{ display: 'flex', gap: 20}}>
|
||||
|
||||
@ -27,6 +27,7 @@
|
||||
"next-themes": "^0.2.1",
|
||||
"oauth4webapi": "^2.10.3",
|
||||
"react-icons": "^5.0.1",
|
||||
"react-spinners-kit": "^1.9.1",
|
||||
"server-only": "^0.0.1",
|
||||
"styled-components": "^6.1.8",
|
||||
"tailwindcss-scoped-preflight": "^2.1.0"
|
||||
|
||||
@ -5,6 +5,7 @@ import { useDesign } from "../providers/design-provider";
|
||||
import Color from 'color';
|
||||
import styled from 'styled-components';
|
||||
import { BORDER_RADIUS, FONT_FAMILY, FONT_SIZES, LINK_COLORS } from "../utils/constants";
|
||||
import { PulseSpinner } from "react-spinners-kit";
|
||||
|
||||
function getColors({
|
||||
propsColor,
|
||||
@ -106,6 +107,7 @@ const StyledButton = styled.button<{
|
||||
$activeBgColor: string,
|
||||
$textColor: string,
|
||||
$underline: boolean,
|
||||
$loading: boolean,
|
||||
}>`
|
||||
border: 0;
|
||||
border-radius: ${BORDER_RADIUS};
|
||||
@ -143,6 +145,7 @@ const StyledButton = styled.button<{
|
||||
}
|
||||
font-family: ${FONT_FAMILY};
|
||||
text-decoration: ${props => props.$underline ? 'underline' : 'none'};
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
@ -150,7 +153,6 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
variant='primary',
|
||||
size='md',
|
||||
loading=false,
|
||||
disabled=false,
|
||||
...props
|
||||
}, ref) => {
|
||||
const { colors, colorMode } = useDesign();
|
||||
@ -168,12 +170,17 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
$bgColor={buttonColors.bgColor}
|
||||
$hoverBgColor={buttonColors.hoverBgColor}
|
||||
$activeBgColor={buttonColors.activeBgColor}
|
||||
$textColor={buttonColors.textColor}
|
||||
$textColor={buttonColors.textColor}
|
||||
$underline={variant === 'link'}
|
||||
disabled={disabled || loading}
|
||||
$loading={loading}
|
||||
{...props}
|
||||
>
|
||||
{props.children}
|
||||
<div style={{ position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%, -50%)', visibility: loading ? 'visible' : 'hidden' }}>
|
||||
<PulseSpinner size={20} color={buttonColors.textColor} />
|
||||
</div>
|
||||
<div style={{ visibility: loading ? 'hidden' : 'visible' }}>
|
||||
{props.children}
|
||||
</div>
|
||||
</StyledButton>
|
||||
);
|
||||
}
|
||||
|
||||
@ -14,7 +14,6 @@ function SettingSection(props: {
|
||||
desc: string,
|
||||
buttonText?: string,
|
||||
buttonDisabled?: boolean,
|
||||
buttonLoading?: boolean,
|
||||
onButtonClick?: React.ComponentProps<typeof Button>["onClick"],
|
||||
buttonVariant?: 'primary' | 'secondary',
|
||||
children?: React.ReactNode,
|
||||
@ -35,7 +34,6 @@ function SettingSection(props: {
|
||||
<Button
|
||||
disabled={props.buttonDisabled}
|
||||
onClick={props.onButtonClick}
|
||||
loading={props.buttonLoading}
|
||||
variant={props.buttonVariant}
|
||||
>
|
||||
{props.buttonText}
|
||||
@ -88,7 +86,6 @@ function ProfileSection() {
|
||||
function EmailVerificationSection() {
|
||||
const user = useUser();
|
||||
const [emailSent, setEmailSent] = useState(false);
|
||||
const [sendingEmail, setSendingEmail] = useState(false);
|
||||
|
||||
return (
|
||||
<SettingSection
|
||||
@ -102,12 +99,9 @@ function EmailVerificationSection() {
|
||||
'Send Email'
|
||||
: undefined
|
||||
}
|
||||
buttonLoading={sendingEmail}
|
||||
onButtonClick={async () => {
|
||||
setSendingEmail(true);
|
||||
await user?.sendVerificationEmail();
|
||||
setEmailSent(true);
|
||||
setSendingEmail(false);
|
||||
}}
|
||||
>
|
||||
{user?.primaryEmailVerified ?
|
||||
@ -123,7 +117,6 @@ function PasswordSection() {
|
||||
const [oldPasswordError, setOldPasswordError] = useState<string>('');
|
||||
const [newPassword, setNewPassword] = useState<string>('');
|
||||
const [newPasswordError, setNewPasswordError] = useState<string>('');
|
||||
const [saving, setSaving] = useState(false);
|
||||
|
||||
if (user?.authMethod !== 'credential') {
|
||||
return null;
|
||||
@ -135,7 +128,6 @@ function PasswordSection() {
|
||||
desc='Change your password'
|
||||
buttonDisabled={!oldPassword || !newPassword}
|
||||
buttonText='Save'
|
||||
buttonLoading={saving}
|
||||
onButtonClick={async () => {
|
||||
if (oldPassword && newPassword) {
|
||||
const errorMessage = getPasswordError(newPassword);
|
||||
|
||||
@ -13,7 +13,6 @@ export default function CredentialSignIn() {
|
||||
const [emailError, setEmailError] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [passwordError, setPasswordError] = useState('');
|
||||
const [loading, setLoading] = useState(false);
|
||||
const app = useStackApp();
|
||||
|
||||
const onSubmit = async () => {
|
||||
@ -30,13 +29,7 @@ export default function CredentialSignIn() {
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
let error;
|
||||
try {
|
||||
error = await app.signInWithCredential({ email, password });
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
const error = await app.signInWithCredential({ email, password });
|
||||
|
||||
if (error instanceof KnownErrors.EmailPasswordMismatch) {
|
||||
setPasswordError('Wrong email or password');
|
||||
@ -79,7 +72,6 @@ export default function CredentialSignIn() {
|
||||
<Button
|
||||
style={{ marginTop: '1.5rem' }}
|
||||
onClick={onSubmit}
|
||||
loading={loading}
|
||||
>
|
||||
Sign In
|
||||
</Button>
|
||||
|
||||
@ -231,6 +231,9 @@ importers:
|
||||
react-icons:
|
||||
specifier: ^5.0.1
|
||||
version: 5.0.1(react@18.2.0)
|
||||
react-spinners-kit:
|
||||
specifier: ^1.9.1
|
||||
version: 1.9.1(styled-components@6.1.8)
|
||||
server-only:
|
||||
specifier: ^0.0.1
|
||||
version: 0.0.1
|
||||
@ -12286,6 +12289,11 @@ packages:
|
||||
find-up: 3.0.0
|
||||
dev: false
|
||||
|
||||
/polished@1.9.3:
|
||||
resolution: {integrity: sha512-4NmSD7fMFlM8roNxs7YXPv7UFRbYzb0gufR5zBxJLRzY54+zFsavxBo6zsQzP9ep6Hh3pC2pTyrpSTBEaB6IkQ==}
|
||||
deprecated: polished@2.X is no longer supported. Please upgrade to @latest for important bug and security fixes.
|
||||
dev: false
|
||||
|
||||
/postcss-calc@8.2.4(postcss@8.4.35):
|
||||
resolution: {integrity: sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==}
|
||||
peerDependencies:
|
||||
@ -13024,6 +13032,18 @@ packages:
|
||||
- vue-template-compiler
|
||||
dev: false
|
||||
|
||||
/react-dom@16.14.0(react@16.14.0):
|
||||
resolution: {integrity: sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==}
|
||||
peerDependencies:
|
||||
react: ^16.14.0
|
||||
dependencies:
|
||||
loose-envify: 1.4.0
|
||||
object-assign: 4.1.1
|
||||
prop-types: 15.8.1
|
||||
react: 16.14.0
|
||||
scheduler: 0.19.1
|
||||
dev: false
|
||||
|
||||
/react-dom@18.2.0(react@18.2.0):
|
||||
resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==}
|
||||
peerDependencies:
|
||||
@ -13243,6 +13263,18 @@ packages:
|
||||
tiny-warning: 1.0.3
|
||||
dev: false
|
||||
|
||||
/react-spinners-kit@1.9.1(styled-components@6.1.8):
|
||||
resolution: {integrity: sha512-QtAvSD7b1WkThY3pRKu6Sr+DZafnEufoOvug/uHprkKyZK6bg6TG5LC3Sy3JaRh6A/HACIcTNEWG+Ls0YDoSHg==}
|
||||
peerDependencies:
|
||||
styled-components: '>=2.0.0'
|
||||
dependencies:
|
||||
polished: 1.9.3
|
||||
prop-types: 15.8.1
|
||||
react: 16.14.0
|
||||
react-dom: 16.14.0(react@16.14.0)
|
||||
styled-components: 6.1.8(react-dom@18.2.0)(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/react-style-singleton@2.2.1(@types/react@18.2.66)(react@18.2.0):
|
||||
resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==}
|
||||
engines: {node: '>=10'}
|
||||
@ -13274,6 +13306,15 @@ packages:
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/react@16.14.0:
|
||||
resolution: {integrity: sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dependencies:
|
||||
loose-envify: 1.4.0
|
||||
object-assign: 4.1.1
|
||||
prop-types: 15.8.1
|
||||
dev: false
|
||||
|
||||
/react@18.2.0:
|
||||
resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@ -13733,6 +13774,13 @@ packages:
|
||||
resolution: {integrity: sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==}
|
||||
dev: false
|
||||
|
||||
/scheduler@0.19.1:
|
||||
resolution: {integrity: sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==}
|
||||
dependencies:
|
||||
loose-envify: 1.4.0
|
||||
object-assign: 4.1.1
|
||||
dev: false
|
||||
|
||||
/scheduler@0.23.0:
|
||||
resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==}
|
||||
dependencies:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user