mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-11-16 01:21:16 +01:00
feat(ui): replace native inputs with Mantine components in forms and settings
- Updated front-ends to use maintine component - Update input such ass PasswordInput etc to use mantine Signed-off-by: Balázs Szücs <bszucs1209@gmail.com>
This commit is contained in:
parent
fb3ec99f0a
commit
e3486f45d0
@ -1,5 +1,4 @@
|
||||
import { useState } from "react";
|
||||
import { Stack, Text, NumberInput, Select, Divider, Checkbox } from "@mantine/core";
|
||||
import { Stack, Text, NumberInput, Select, Divider, Checkbox, Slider } from "@mantine/core";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { CompressParameters } from "@app/hooks/tools/compress/useCompressParameters";
|
||||
import ButtonSelector from "@app/components/shared/ButtonSelector";
|
||||
@ -12,7 +11,6 @@ interface CompressSettingsProps {
|
||||
|
||||
const CompressSettings = ({ parameters, onParameterChange, disabled = false }: CompressSettingsProps) => {
|
||||
const { t } = useTranslation();
|
||||
const [isSliding, setIsSliding] = useState(false);
|
||||
|
||||
return (
|
||||
<Stack gap="md">
|
||||
@ -35,50 +33,19 @@ const CompressSettings = ({ parameters, onParameterChange, disabled = false }: C
|
||||
<Stack gap="sm">
|
||||
<Divider />
|
||||
<Text size="sm" fw={500}>Compression Level</Text>
|
||||
<div style={{ position: 'relative' }}>
|
||||
<input
|
||||
type="range"
|
||||
min="1"
|
||||
max="9"
|
||||
step="1"
|
||||
value={parameters.compressionLevel}
|
||||
onChange={(e) => onParameterChange('compressionLevel', parseInt(e.target.value))}
|
||||
onMouseDown={() => setIsSliding(true)}
|
||||
onMouseUp={() => setIsSliding(false)}
|
||||
onTouchStart={() => setIsSliding(true)}
|
||||
onTouchEnd={() => setIsSliding(false)}
|
||||
disabled={disabled}
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '6px',
|
||||
borderRadius: '3px',
|
||||
background: `linear-gradient(to right, #228be6 0%, #228be6 ${(parameters.compressionLevel - 1) / 8 * 100}%, #e9ecef ${(parameters.compressionLevel - 1) / 8 * 100}%, #e9ecef 100%)`,
|
||||
outline: 'none',
|
||||
WebkitAppearance: 'none'
|
||||
}}
|
||||
/>
|
||||
{isSliding && (
|
||||
<div style={{
|
||||
position: 'absolute',
|
||||
top: '-25px',
|
||||
left: `${(parameters.compressionLevel - 1) / 8 * 100}%`,
|
||||
transform: 'translateX(-50%)',
|
||||
background: '#f8f9fa',
|
||||
border: '1px solid #dee2e6',
|
||||
borderRadius: '4px',
|
||||
padding: '2px 6px',
|
||||
fontSize: '12px',
|
||||
color: '#228be6',
|
||||
whiteSpace: 'nowrap'
|
||||
}}>
|
||||
{parameters.compressionLevel}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', fontSize: '12px', color: '#6c757d' }}>
|
||||
<span>Min 1</span>
|
||||
<span>Max 9</span>
|
||||
</div>
|
||||
<Slider
|
||||
min={1}
|
||||
max={9}
|
||||
step={1}
|
||||
value={parameters.compressionLevel}
|
||||
onChange={(value) => onParameterChange('compressionLevel', value)}
|
||||
disabled={disabled}
|
||||
marks={[
|
||||
{ value: 1, label: 'Min 1' },
|
||||
{ value: 9, label: 'Max 9' },
|
||||
]}
|
||||
label={(value) => `${value}`}
|
||||
/>
|
||||
<Text size="xs" c="dimmed" style={{ marginTop: '8px' }}>
|
||||
{parameters.compressionLevel <= 3 && "1-3 PDF compression"}
|
||||
{parameters.compressionLevel >= 4 && parameters.compressionLevel <= 6 && "4-6 lite image compression"}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import '@app/routes/authShared/auth.css';
|
||||
import { TextInput, PasswordInput, Button } from '@mantine/core';
|
||||
|
||||
interface EmailPasswordFormProps {
|
||||
email: string
|
||||
@ -38,49 +39,46 @@ export default function EmailPasswordForm({
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="auth-fields">
|
||||
<div className="auth-field">
|
||||
<label htmlFor="email" className="auth-label">{t('login.username', 'Username')}</label>
|
||||
<input
|
||||
<TextInput
|
||||
id="email"
|
||||
label={t('login.username', 'Username')}
|
||||
type="text"
|
||||
name="username"
|
||||
autoComplete="username"
|
||||
placeholder={t('login.enterUsername', 'Enter username')}
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
className={`auth-input ${fieldErrors.email ? 'auth-input-error' : ''}`}
|
||||
error={fieldErrors.email}
|
||||
classNames={{ label: 'auth-label' }}
|
||||
/>
|
||||
{fieldErrors.email && (
|
||||
<div className="auth-field-error">{fieldErrors.email}</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{showPasswordField && (
|
||||
<div className="auth-field">
|
||||
<label htmlFor="password" className="auth-label">{t('login.password')}</label>
|
||||
<input
|
||||
<PasswordInput
|
||||
id="password"
|
||||
type="password"
|
||||
label={t('login.password')}
|
||||
name="current-password"
|
||||
autoComplete="current-password"
|
||||
placeholder={t('login.enterPassword')}
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
className={`auth-input ${fieldErrors.password ? 'auth-input-error' : ''}`}
|
||||
error={fieldErrors.password}
|
||||
classNames={{ label: 'auth-label' }}
|
||||
/>
|
||||
{fieldErrors.password && (
|
||||
<div className="auth-field-error">{fieldErrors.password}</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<button
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={isSubmitting || !email || (showPasswordField && !password)}
|
||||
className="auth-button"
|
||||
fullWidth
|
||||
loading={isSubmitting}
|
||||
>
|
||||
{submitButtonText}
|
||||
</button>
|
||||
</Button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import { Button } from '@mantine/core';
|
||||
|
||||
interface NavigationLinkProps {
|
||||
onClick: () => void
|
||||
text: string
|
||||
@ -7,13 +9,14 @@ interface NavigationLinkProps {
|
||||
export default function NavigationLink({ onClick, text, isDisabled = false }: NavigationLinkProps) {
|
||||
return (
|
||||
<div className="navigation-link-container">
|
||||
<button
|
||||
<Button
|
||||
onClick={onClick}
|
||||
disabled={isDisabled}
|
||||
className="navigation-link-button"
|
||||
variant="subtle"
|
||||
>
|
||||
{text}
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { BASE_PATH } from '@app/constants/app';
|
||||
import { Button } from '@mantine/core';
|
||||
|
||||
// OAuth provider configuration
|
||||
const oauthProviders = [
|
||||
@ -26,14 +27,15 @@ export default function OAuthButtons({ onProviderClick, isSubmitting, layout = '
|
||||
<div className="oauth-container-icons">
|
||||
{enabledProviders.map((p) => (
|
||||
<div key={p.id} title={`${t('login.signInWith', 'Sign in with')} ${p.label}`}>
|
||||
<button
|
||||
<Button
|
||||
onClick={() => onProviderClick(p.id as any)}
|
||||
disabled={isSubmitting}
|
||||
className="oauth-button-icon"
|
||||
aria-label={`${t('login.signInWith', 'Sign in with')} ${p.label}`}
|
||||
variant="default"
|
||||
>
|
||||
<img src={`${BASE_PATH}/Login/${p.file}`} alt={p.label} className="oauth-icon-small"/>
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@ -45,14 +47,15 @@ export default function OAuthButtons({ onProviderClick, isSubmitting, layout = '
|
||||
<div className="oauth-container-grid">
|
||||
{enabledProviders.map((p) => (
|
||||
<div key={p.id} title={`${t('login.signInWith', 'Sign in with')} ${p.label}`}>
|
||||
<button
|
||||
<Button
|
||||
onClick={() => onProviderClick(p.id as any)}
|
||||
disabled={isSubmitting}
|
||||
className="oauth-button-grid"
|
||||
aria-label={`${t('login.signInWith', 'Sign in with')} ${p.label}`}
|
||||
variant="default"
|
||||
>
|
||||
<img src={`${BASE_PATH}/Login/${p.file}`} alt={p.label} className="oauth-icon-medium"/>
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@ -62,16 +65,17 @@ export default function OAuthButtons({ onProviderClick, isSubmitting, layout = '
|
||||
return (
|
||||
<div className="oauth-container-vertical">
|
||||
{enabledProviders.map((p) => (
|
||||
<button
|
||||
<Button
|
||||
key={p.id}
|
||||
onClick={() => onProviderClick(p.id as any)}
|
||||
disabled={isSubmitting}
|
||||
className="oauth-button-vertical"
|
||||
title={p.label}
|
||||
variant="default"
|
||||
leftSection={<img src={`${BASE_PATH}/Login/${p.file}`} alt={p.label} className="oauth-icon-tiny" />}
|
||||
>
|
||||
<img src={`${BASE_PATH}/Login/${p.file}`} alt={p.label} className="oauth-icon-tiny" />
|
||||
{p.label}
|
||||
</button>
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useEffect } from 'react';
|
||||
import '@app/routes/authShared/auth.css';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Checkbox } from '@mantine/core';
|
||||
import { Checkbox, TextInput, PasswordInput, Button } from '@mantine/core';
|
||||
import { SignupFieldErrors } from '@app/routes/signup/SignupFormValidation';
|
||||
|
||||
interface SignupFormProps {
|
||||
@ -53,57 +53,49 @@ export default function SignupForm({
|
||||
<div className="auth-fields">
|
||||
{showName && (
|
||||
<div className="auth-field">
|
||||
<label htmlFor="name" className="auth-label">{t('signup.name')}</label>
|
||||
<input
|
||||
<TextInput
|
||||
id="name"
|
||||
type="text"
|
||||
label={t('signup.name')}
|
||||
name="name"
|
||||
autoComplete="name"
|
||||
placeholder={t('signup.enterName')}
|
||||
value={name}
|
||||
onChange={(e) => setName?.(e.target.value)}
|
||||
className={`auth-input ${fieldErrors.name ? 'auth-input-error' : ''}`}
|
||||
error={fieldErrors.name}
|
||||
classNames={{ label: 'auth-label' }}
|
||||
/>
|
||||
{fieldErrors.name && (
|
||||
<div className="auth-field-error">{fieldErrors.name}</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="auth-field">
|
||||
<label htmlFor="email" className="auth-label">{t('signup.email')}</label>
|
||||
<input
|
||||
<TextInput
|
||||
id="email"
|
||||
label={t('signup.email')}
|
||||
type="email"
|
||||
name="email"
|
||||
autoComplete="email"
|
||||
placeholder={t('signup.enterEmail')}
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
onKeyPress={(e) => e.key === 'Enter' && !isSubmitting && onSubmit()}
|
||||
className={`auth-input ${fieldErrors.email ? 'auth-input-error' : ''}`}
|
||||
onKeyDown={(e) => e.key === 'Enter' && !isSubmitting && onSubmit()}
|
||||
error={fieldErrors.email}
|
||||
classNames={{ label: 'auth-label' }}
|
||||
/>
|
||||
{fieldErrors.email && (
|
||||
<div className="auth-field-error">{fieldErrors.email}</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="auth-field">
|
||||
<label htmlFor="password" className="auth-label">{t('signup.password')}</label>
|
||||
<input
|
||||
<PasswordInput
|
||||
id="password"
|
||||
type="password"
|
||||
label={t('signup.password')}
|
||||
name="new-password"
|
||||
autoComplete="new-password"
|
||||
placeholder={t('signup.enterPassword')}
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
onKeyPress={(e) => e.key === 'Enter' && !isSubmitting && onSubmit()}
|
||||
className={`auth-input ${fieldErrors.password ? 'auth-input-error' : ''}`}
|
||||
onKeyDown={(e) => e.key === 'Enter' && !isSubmitting && onSubmit()}
|
||||
error={fieldErrors.password}
|
||||
classNames={{ label: 'auth-label' }}
|
||||
/>
|
||||
{fieldErrors.password && (
|
||||
<div className="auth-field-error">{fieldErrors.password}</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div
|
||||
@ -112,21 +104,18 @@ export default function SignupForm({
|
||||
style={{ maxHeight: showConfirm ? 96 : 0, opacity: showConfirm ? 1 : 0 }}
|
||||
>
|
||||
<div className="auth-field">
|
||||
<label htmlFor="confirmPassword" className="auth-label">{t('signup.confirmPassword')}</label>
|
||||
<input
|
||||
<PasswordInput
|
||||
id="confirmPassword"
|
||||
type="password"
|
||||
label={t('signup.confirmPassword')}
|
||||
name="new-password"
|
||||
autoComplete="new-password"
|
||||
placeholder={t('signup.confirmPasswordPlaceholder')}
|
||||
value={confirmPassword}
|
||||
onChange={(e) => setConfirmPassword(e.target.value)}
|
||||
onKeyPress={(e) => e.key === 'Enter' && !isSubmitting && onSubmit()}
|
||||
className={`auth-input ${fieldErrors.confirmPassword ? 'auth-input-error' : ''}`}
|
||||
onKeyDown={(e) => e.key === 'Enter' && !isSubmitting && onSubmit()}
|
||||
error={fieldErrors.confirmPassword}
|
||||
classNames={{ label: 'auth-label' }}
|
||||
/>
|
||||
{fieldErrors.confirmPassword && (
|
||||
<div className="auth-field-error">{fieldErrors.confirmPassword}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -152,13 +141,15 @@ export default function SignupForm({
|
||||
)}
|
||||
|
||||
{/* Sign Up Button */}
|
||||
<button
|
||||
<Button
|
||||
onClick={onSubmit}
|
||||
disabled={isSubmitting || !email || !password || !confirmPassword || (showTerms && !agree)}
|
||||
className="auth-button"
|
||||
fullWidth
|
||||
loading={isSubmitting}
|
||||
>
|
||||
{isSubmitting ? t('signup.creatingAccount') : t('signup.signUp')}
|
||||
</button>
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user