refactor: button to allow html attributes & refactor class names (#956)

This commit is contained in:
Aman Harwara
2022-03-28 17:41:54 +05:30
committed by GitHub
parent 3ca7102fd0
commit aba1e35a1d
31 changed files with 117 additions and 90 deletions

View File

@@ -130,7 +130,7 @@ export const ConfirmPassword: FunctionComponent<Props> = observer(
label={ label={
isRegistering ? 'Creating account...' : 'Create account & sign in' isRegistering ? 'Creating account...' : 'Create account & sign in'
} }
type="primary" variant="primary"
onClick={handleConfirmFormSubmit} onClick={handleConfirmFormSubmit}
disabled={isRegistering} disabled={isRegistering}
/> />

View File

@@ -122,7 +122,7 @@ export const CreateAccount: FunctionComponent<Props> = observer(
<Button <Button
className="btn-w-full mt-1" className="btn-w-full mt-1"
label="Next" label="Next"
type="primary" variant="primary"
onClick={handleRegisterFormSubmit} onClick={handleRegisterFormSubmit}
/> />
</form> </form>

View File

@@ -171,7 +171,7 @@ export const SignInPane: FunctionComponent<Props> = observer(
<Button <Button
className="btn-w-full mt-1 mb-3" className="btn-w-full mt-1 mb-3"
label={isSigningIn ? 'Signing in...' : 'Sign in'} label={isSigningIn ? 'Signing in...' : 'Sign in'}
type="primary" variant="primary"
onClick={handleSignInFormSubmit} onClick={handleSignInFormSubmit}
disabled={isSigningIn} disabled={isSigningIn}
/> />

View File

@@ -196,7 +196,7 @@ export const AttachedFilesPopover: FunctionComponent<Props> = observer(
: 'No files found in this account'} : 'No files found in this account'}
</div> </div>
<Button <Button
type="normal" variant="normal"
onClick={handleAttachFilesClick} onClick={handleAttachFilesClick}
onBlur={closeOnBlur} onBlur={closeOnBlur}
> >

View File

@@ -1,60 +1,79 @@
import { JSXInternal } from 'preact/src/jsx'; import { JSXInternal } from 'preact/src/jsx';
import TargetedEvent = JSXInternal.TargetedEvent;
import TargetedMouseEvent = JSXInternal.TargetedMouseEvent;
import { ComponentChildren, FunctionComponent, Ref } from 'preact'; import { ComponentChildren, FunctionComponent, Ref } from 'preact';
import { forwardRef } from 'preact/compat'; import { forwardRef } from 'preact/compat';
const baseClass = `rounded px-4 py-1.75 font-bold text-sm fit-content`; const baseClass = `rounded px-4 py-1.75 font-bold text-sm fit-content`;
type ButtonType = 'normal' | 'primary' | 'danger'; type ButtonVariant = 'normal' | 'primary';
const buttonClasses: { [type in ButtonType]: string } = { const getClassName = (
normal: `${baseClass} bg-default color-text border-solid border-main border-1 focus:bg-contrast hover:bg-contrast`, variant: ButtonVariant,
primary: `${baseClass} no-border bg-info color-info-contrast hover:brightness-130 focus:brightness-130`, danger: boolean,
danger: `${baseClass} bg-default color-danger border-solid border-main border-1 focus:bg-contrast hover:bg-contrast`, disabled: boolean
) => {
const borders =
variant === 'normal' ? 'border-solid border-main border-1' : 'no-border';
const cursor = disabled ? 'cursor-not-allowed' : 'cursor-pointer';
let colors =
variant === 'normal'
? 'bg-default color-text'
: 'bg-info color-info-contrast';
let focusHoverStates =
variant === 'normal'
? 'focus:bg-contrast hover:bg-contrast'
: 'hover:brightness-130 focus:brightness-130';
if (danger) {
colors =
variant === 'normal'
? 'bg-default color-danger'
: 'bg-danger color-info-contrast';
}
if (disabled) {
colors =
variant === 'normal'
? 'bg-default color-grey-2'
: 'bg-grey-2 color-info-contrast';
focusHoverStates =
variant === 'normal'
? 'focus:bg-default hover:bg-default'
: 'focus:brightness-default hover:brightness-default';
}
return `${baseClass} ${colors} ${borders} ${focusHoverStates} ${cursor}`;
}; };
type ButtonProps = { type ButtonProps = JSXInternal.HTMLAttributes<HTMLButtonElement> & {
children?: ComponentChildren; children?: ComponentChildren;
className?: string; className?: string;
type: ButtonType; variant?: ButtonVariant;
dangerStyle?: boolean;
label?: string; label?: string;
onClick: (
event:
| TargetedEvent<HTMLFormElement>
| TargetedMouseEvent<HTMLButtonElement>
) => void;
onBlur?: (event: FocusEvent) => void;
disabled?: boolean;
}; };
export const Button: FunctionComponent<ButtonProps> = forwardRef( export const Button: FunctionComponent<ButtonProps> = forwardRef(
( (
{ {
type, variant = 'normal',
label, label,
className = '', className = '',
onBlur, dangerStyle: danger = false,
onClick,
disabled = false, disabled = false,
children, children,
...props
}: ButtonProps, }: ButtonProps,
ref: Ref<HTMLButtonElement> ref: Ref<HTMLButtonElement>
) => { ) => {
const buttonClass = buttonClasses[type];
const cursorClass = disabled ? 'cursor-default' : 'cursor-pointer';
return ( return (
<button <button
className={`${buttonClass} ${cursorClass} ${className}`} type="button"
onBlur={onBlur} className={`${getClassName(variant, danger, disabled)} ${className}`}
onClick={(e) => {
onClick(e);
e.preventDefault();
}}
disabled={disabled} disabled={disabled}
ref={ref} ref={ref}
{...props}
> >
{label ?? children} {label ?? children}
</button> </button>

View File

@@ -111,7 +111,7 @@ export const FilePreviewModal: FunctionComponent<Props> = ({
<div className="flex items-center"> <div className="flex items-center">
{objectUrl && ( {objectUrl && (
<Button <Button
type="primary" variant="primary"
className="mr-4" className="mr-4"
onClick={() => { onClick={() => {
application application
@@ -151,7 +151,7 @@ export const FilePreviewModal: FunctionComponent<Props> = ({
</div> </div>
<div className="flex items-center"> <div className="flex items-center">
<Button <Button
type="primary" variant="primary"
className="mr-3" className="mr-3"
onClick={() => { onClick={() => {
getObjectUrl(); getObjectUrl();
@@ -160,7 +160,7 @@ export const FilePreviewModal: FunctionComponent<Props> = ({
Try again Try again
</Button> </Button>
<Button <Button
type="normal" variant="normal"
onClick={() => { onClick={() => {
application.getAppState().files.downloadFile(file); application.getAppState().files.downloadFile(file);
}} }}
@@ -176,7 +176,7 @@ export const FilePreviewModal: FunctionComponent<Props> = ({
application. application.
</div> </div>
<Button <Button
type="primary" variant="primary"
onClick={() => { onClick={() => {
application.getAppState().files.downloadFile(file); application.getAppState().files.downloadFile(file);
}} }}

View File

@@ -120,7 +120,7 @@ export const Extensions: FunctionComponent<{
<div className="min-h-2" /> <div className="min-h-2" />
<Button <Button
className="min-w-20" className="min-w-20"
type="normal" variant="normal"
label="Install" label="Install"
onClick={() => submitExtensionUrl(customUrl)} onClick={() => submitExtensionUrl(customUrl)}
/> />

View File

@@ -102,7 +102,7 @@ export const Listed = observer(({ application }: Props) => {
<Text>Create a free Listed author account to get started.</Text> <Text>Create a free Listed author account to get started.</Text>
<Button <Button
className="mt-3" className="mt-3"
type="normal" variant="normal"
disabled={requestingAccount} disabled={requestingAccount}
label={ label={
requestingAccount ? 'Creating account...' : 'Create new author' requestingAccount ? 'Creating account...' : 'Create new author'

View File

@@ -39,7 +39,7 @@ export const Authentication: FunctionComponent<{
and enable end-to-end encryption. and enable end-to-end encryption.
</Text> </Text>
<Button <Button
type="primary" variant="primary"
label="Create free account" label="Create free account"
onClick={clickRegister} onClick={clickRegister}
className="mb-3" className="mb-3"

View File

@@ -50,7 +50,7 @@ export const Credentials: FunctionComponent<Props> = observer(
</Text> </Text>
<Button <Button
className="min-w-20 mt-3" className="min-w-20 mt-3"
type="normal" variant="normal"
label="Change email" label="Change email"
onClick={() => { onClick={() => {
setIsChangeEmailDialogOpen(true); setIsChangeEmailDialogOpen(true);
@@ -64,7 +64,7 @@ export const Credentials: FunctionComponent<Props> = observer(
</Text> </Text>
<Button <Button
className="min-w-20 mt-3" className="min-w-20 mt-3"
type="normal" variant="normal"
label="Change password" label="Change password"
onClick={presentPasswordWizard} onClick={presentPasswordWizard}
/> />

View File

@@ -27,14 +27,14 @@ const SignOutView: FunctionComponent<{
<div className="flex flex-row"> <div className="flex flex-row">
<Button <Button
className="mr-3" className="mr-3"
type="normal" variant="normal"
label="Sign out other sessions" label="Sign out other sessions"
onClick={() => { onClick={() => {
appState.accountMenu.setOtherSessionsSignOut(true); appState.accountMenu.setOtherSessionsSignOut(true);
}} }}
/> />
<Button <Button
type="normal" variant="normal"
label="Manage sessions" label="Manage sessions"
onClick={() => appState.openSessionsModal()} onClick={() => appState.openSessionsModal()}
/> />
@@ -48,7 +48,7 @@ const SignOutView: FunctionComponent<{
</Text> </Text>
<div className="min-h-3" /> <div className="min-h-3" />
<Button <Button
type="danger" dangerStyle={true}
label="Sign out workspace" label="Sign out workspace"
onClick={() => { onClick={() => {
appState.accountMenu.setSigningOut(true); appState.accountMenu.setSigningOut(true);
@@ -76,7 +76,7 @@ const ClearSessionDataView: FunctionComponent<{
</Text> </Text>
<div className="min-h-3" /> <div className="min-h-3" />
<Button <Button
type="danger" dangerStyle={true}
label="Clear workspace" label="Clear workspace"
onClick={() => { onClick={() => {
appState.accountMenu.setSigningOut(true); appState.accountMenu.setSigningOut(true);

View File

@@ -55,7 +55,7 @@ export const Sync: FunctionComponent<Props> = observer(
</Text> </Text>
<Button <Button
className="min-w-20 mt-3" className="min-w-20 mt-3"
type="normal" variant="normal"
label="Sync now" label="Sync now"
disabled={isSyncingInProgress} disabled={isSyncingInProgress}
onClick={doSynchronization} onClick={doSynchronization}

View File

@@ -169,7 +169,7 @@ export const ChangeEmail: FunctionalComponent<Props> = ({
<ModalDialogButtons className="px-4.5"> <ModalDialogButtons className="px-4.5">
<Button <Button
className="min-w-20" className="min-w-20"
type="primary" variant="primary"
label={submitButtonTitle} label={submitButtonTitle}
onClick={handleSubmit} onClick={handleSubmit}
/> />

View File

@@ -2,8 +2,6 @@ import { FunctionalComponent } from 'preact';
import { Subtitle } from '@/components/Preferences/components'; import { Subtitle } from '@/components/Preferences/components';
import { DecoratedInput } from '@/components/DecoratedInput'; import { DecoratedInput } from '@/components/DecoratedInput';
import { Button } from '@/components/Button'; import { Button } from '@/components/Button';
import { JSXInternal } from '@node_modules/preact/src/jsx';
import TargetedEvent = JSXInternal.TargetedEvent;
import { useEffect, useState } from 'preact/hooks'; import { useEffect, useState } from 'preact/hooks';
import { WebApplication } from '@/ui_models/application'; import { WebApplication } from '@/ui_models/application';
import { AppState } from '@/ui_models/app_state'; import { AppState } from '@/ui_models/app_state';
@@ -40,9 +38,7 @@ export const OfflineSubscription: FunctionalComponent<IProps> = observer(
); );
}; };
const handleSubscriptionCodeSubmit = async ( const handleSubscriptionCodeSubmit = async (event: Event) => {
event: TargetedEvent<HTMLFormElement, Event>
) => {
event.preventDefault(); event.preventDefault();
const result = await application.features.setOfflineFeaturesCode( const result = await application.features.setOfflineFeaturesCode(
@@ -117,7 +113,7 @@ export const OfflineSubscription: FunctionalComponent<IProps> = observer(
)} )}
{hasUserPreviouslyStoredCode && ( {hasUserPreviouslyStoredCode && (
<Button <Button
type="danger" dangerStyle={true}
label="Remove offline key" label="Remove offline key"
onClick={() => { onClick={() => {
handleRemoveClick(); handleRemoveClick();
@@ -127,13 +123,9 @@ export const OfflineSubscription: FunctionalComponent<IProps> = observer(
{!hasUserPreviouslyStoredCode && !isSuccessfullyActivated && ( {!hasUserPreviouslyStoredCode && !isSuccessfullyActivated && (
<Button <Button
label={'Submit'} label={'Submit'}
type="primary" variant="primary"
disabled={activationCode === ''} disabled={activationCode === ''}
onClick={(event) => onClick={(event) => handleSubscriptionCodeSubmit(event)}
handleSubscriptionCodeSubmit(
event as TargetedEvent<HTMLFormElement>
)
}
/> />
)} )}
</form> </form>

View File

@@ -46,7 +46,7 @@ export const NoSubscription: FunctionalComponent<{
{application.hasAccount() && ( {application.hasAccount() && (
<Button <Button
className="min-w-20 mt-3" className="min-w-20 mt-3"
type="primary" variant="primary"
label="Subscribe" label="Subscribe"
onClick={onPurchaseClick} onClick={onPurchaseClick}
/> />

View File

@@ -81,7 +81,7 @@ export const SubscriptionInformation = observer(
<StatusText subscriptionState={subscriptionState} /> <StatusText subscriptionState={subscriptionState} />
<Button <Button
className="min-w-20 mt-3 mr-3" className="min-w-20 mt-3 mr-3"
type="normal" variant="normal"
label="Manage subscription" label="Manage subscription"
onClick={manageSubscription} onClick={manageSubscription}
/> />

View File

@@ -201,7 +201,7 @@ export const DataBackups = observer(({ application, appState }: Props) => {
)} )}
<Button <Button
type="normal" variant="normal"
onClick={downloadDataArchive} onClick={downloadDataArchive}
label="Download backup" label="Download backup"
className="mt-2" className="mt-2"
@@ -212,7 +212,7 @@ export const DataBackups = observer(({ application, appState }: Props) => {
<div class="flex flex-row items-center mt-3"> <div class="flex flex-row items-center mt-3">
<Button <Button
type="normal" variant="normal"
label="Import backup" label="Import backup"
onClick={handleImportFile} onClick={handleImportFile}
/> />

View File

@@ -222,7 +222,7 @@ export const CloudBackupProvider: FunctionComponent<Props> = ({
{shouldShowEnableButton && ( {shouldShowEnableButton && (
<div> <div>
<Button <Button
type="normal" variant="normal"
label="Enable" label="Enable"
className={`px-1 text-xs min-w-40 ${additionalClass}`} className={`px-1 text-xs min-w-40 ${additionalClass}`}
onClick={installIntegration} onClick={installIntegration}
@@ -235,13 +235,13 @@ export const CloudBackupProvider: FunctionComponent<Props> = ({
<div className={'flex flex-col items-end'}> <div className={'flex flex-col items-end'}>
<Button <Button
className={`min-w-40 mb-2 ${additionalClass}`} className={`min-w-40 mb-2 ${additionalClass}`}
type="normal" variant="normal"
label="Perform Backup" label="Perform Backup"
onClick={performBackupNow} onClick={performBackupNow}
/> />
<Button <Button
className="min-w-40" className="min-w-40"
type="normal" variant="normal"
label="Disable" label="Disable"
onClick={disable} onClick={disable}
/> />

View File

@@ -56,7 +56,7 @@ export const ConfirmCustomExtension: FunctionComponent<{
<div className="flex flex-row"> <div className="flex flex-row">
<Button <Button
className="min-w-20" className="min-w-20"
type="normal" variant="normal"
label="Cancel" label="Cancel"
onClick={() => callback(false)} onClick={() => callback(false)}
/> />
@@ -65,7 +65,7 @@ export const ConfirmCustomExtension: FunctionComponent<{
<Button <Button
className="min-w-20" className="min-w-20"
type="normal" variant="normal"
label="Install" label="Install"
onClick={() => callback(true)} onClick={() => callback(true)}
/> />

View File

@@ -104,7 +104,7 @@ export const ExtensionItem: FunctionComponent<ExtensionItemProps> = ({
<div className="flex flex-row"> <div className="flex flex-row">
<Button <Button
className="min-w-20" className="min-w-20"
type="normal" variant="normal"
label="Uninstall" label="Uninstall"
onClick={() => uninstall(extension)} onClick={() => uninstall(extension)}
/> />

View File

@@ -224,7 +224,7 @@ export const PasscodeLock = observer(({ application, appState }: Props) => {
<Button <Button
label="Add passcode" label="Add passcode"
onClick={handleAddPassCode} onClick={handleAddPassCode}
type="primary" variant="primary"
/> />
)} )}
</> </>
@@ -258,13 +258,13 @@ export const PasscodeLock = observer(({ application, appState }: Props) => {
/> />
<div className="min-h-2" /> <div className="min-h-2" />
<Button <Button
type="primary" variant="primary"
onClick={submitPasscodeForm} onClick={submitPasscodeForm}
label="Set Passcode" label="Set Passcode"
className="mr-3" className="mr-3"
/> />
<Button <Button
type="normal" variant="normal"
onClick={() => setShowPasscodeForm(false)} onClick={() => setShowPasscodeForm(false)}
label="Cancel" label="Cancel"
/> />
@@ -276,13 +276,13 @@ export const PasscodeLock = observer(({ application, appState }: Props) => {
<Text>Passcode lock is enabled.</Text> <Text>Passcode lock is enabled.</Text>
<div className="flex flex-row mt-3"> <div className="flex flex-row mt-3">
<Button <Button
type="normal" variant="normal"
label="Change Passcode" label="Change Passcode"
onClick={changePasscodePressed} onClick={changePasscodePressed}
className="mr-3" className="mr-3"
/> />
<Button <Button
type="danger" dangerStyle={true}
label="Remove Passcode" label="Remove Passcode"
onClick={removePasscodePressed} onClick={removePasscodePressed}
/> />

View File

@@ -107,7 +107,7 @@ export const Protections: FunctionalComponent<Props> = ({ application }) => {
{protectionsDisabledUntil && ( {protectionsDisabledUntil && (
<Button <Button
className="mt-3" className="mt-3"
type="primary" variant="primary"
label="End Unprotected Access" label="End Unprotected Access"
onClick={enableProtections} onClick={enableProtections}
/> />

View File

@@ -78,13 +78,13 @@ export const SaveSecretKey: FunctionComponent<{
<ModalDialogButtons> <ModalDialogButtons>
<Button <Button
className="min-w-20" className="min-w-20"
type="normal" variant="normal"
label="Back" label="Back"
onClick={() => act.openScanQRCode()} onClick={() => act.openScanQRCode()}
/> />
<Button <Button
className="min-w-20" className="min-w-20"
type="primary" variant="primary"
label="Next" label="Next"
onClick={() => act.openVerification()} onClick={() => act.openVerification()}
/> />

View File

@@ -63,13 +63,13 @@ export const ScanQRCode: FunctionComponent<{
<ModalDialogButtons> <ModalDialogButtons>
<Button <Button
className="min-w-20" className="min-w-20"
type="normal" variant="normal"
label="Cancel" label="Cancel"
onClick={() => act.cancelActivation()} onClick={() => act.cancelActivation()}
/> />
<Button <Button
className="min-w-20" className="min-w-20"
type="primary" variant="primary"
label="Next" label="Next"
onClick={() => act.openSaveSecretKey()} onClick={() => act.openSaveSecretKey()}
/> />

View File

@@ -27,7 +27,7 @@ export const TwoFactorSuccess: FunctionComponent<{
<ModalDialogButtons> <ModalDialogButtons>
<Button <Button
className="min-w-20" className="min-w-20"
type="primary" variant="primary"
label="Finish" label="Finish"
onClick={act.finishActivation} onClick={act.finishActivation}
/> />

View File

@@ -65,13 +65,13 @@ export const Verification: FunctionComponent<{
)} )}
<Button <Button
className="min-w-20" className="min-w-20"
type="normal" variant="normal"
label="Back" label="Back"
onClick={act.openSaveSecretKey} onClick={act.openSaveSecretKey}
/> />
<Button <Button
className="min-w-20" className="min-w-20"
type="primary" variant="primary"
label="Next" label="Next"
onClick={act.enable2FA} onClick={act.enable2FA}
/> />

View File

@@ -204,7 +204,7 @@ export const CreateAccount: FunctionComponent<Props> = observer(
</div> </div>
<Button <Button
className="py-2.5 xs:mb-4" className="py-2.5 xs:mb-4"
type="primary" variant="primary"
label={ label={
isCreatingAccount ? 'Creating account...' : 'Create account' isCreatingAccount ? 'Creating account...' : 'Create account'
} }

View File

@@ -151,7 +151,7 @@ export const SignIn: FunctionComponent<Props> = observer(
</div> </div>
<Button <Button
className={`${isSigningIn ? 'min-w-30' : 'min-w-24'} py-2.5 mb-5`} className={`${isSigningIn ? 'min-w-30' : 'min-w-24'} py-2.5 mb-5`}
type="primary" variant="primary"
label={isSigningIn ? 'Signing in...' : 'Sign in'} label={isSigningIn ? 'Signing in...' : 'Sign in'}
onClick={handleSignIn} onClick={handleSignIn}
disabled={isSigningIn} disabled={isSigningIn}

View File

@@ -46,7 +46,7 @@ export const RevisionContentLocked: FunctionComponent<{
. Learn more about our other plans to upgrade your history capacity. . Learn more about our other plans to upgrade your history capacity.
</div> </div>
<Button <Button
type="primary" variant="primary"
label="Discover plans" label="Discover plans"
onClick={() => { onClick={() => {
if (window.plansUrl) { if (window.plansUrl) {

View File

@@ -292,7 +292,7 @@ export const RevisionHistoryModal: FunctionComponent<RevisionHistoryModalProps>
label="Close" label="Close"
onClick={dismissModal} onClick={dismissModal}
ref={closeButtonRef} ref={closeButtonRef}
type="normal" variant="normal"
/> />
</div> </div>
{selectedRevision && ( {selectedRevision && (
@@ -301,7 +301,7 @@ export const RevisionHistoryModal: FunctionComponent<RevisionHistoryModalProps>
<Button <Button
className="py-1.35 mr-2.5" className="py-1.35 mr-2.5"
onClick={deleteSelectedRevision} onClick={deleteSelectedRevision}
type="normal" variant="normal"
> >
{isDeletingRevision ? ( {isDeletingRevision ? (
<div className="sk-spinner my-1 w-3 h-3 spinner-info" /> <div className="sk-spinner my-1 w-3 h-3 spinner-info" />
@@ -314,13 +314,13 @@ export const RevisionHistoryModal: FunctionComponent<RevisionHistoryModalProps>
className="py-1.35 mr-2.5" className="py-1.35 mr-2.5"
label="Restore as a copy" label="Restore as a copy"
onClick={restoreAsCopy} onClick={restoreAsCopy}
type="normal" variant="normal"
/> />
<Button <Button
className="py-1.35" className="py-1.35"
label="Restore version" label="Restore version"
onClick={restore} onClick={restore}
type="primary" variant="primary"
/> />
</div> </div>
)} )}

View File

@@ -1045,3 +1045,19 @@
.vertical-middle { .vertical-middle {
vertical-align: middle; vertical-align: middle;
} }
.sn-component .focus\:brightness-default:focus {
filter: brightness(100%);
}
.sn-component .hover\:brightness-default:hover {
filter: brightness(100%);
}
.sn-component .focus\:bg-default:focus {
background-color: var(--sn-stylekit-background-color);
}
.sn-component .hover\:bg-default:hover {
background-color: var(--sn-stylekit-background-color);
}