Skip to content

Commit e3be146

Browse files
committed
fix[frontend]: blink on toggle daro mode due to inconsistent transition-duration
1 parent ee82358 commit e3be146

File tree

8 files changed

+90
-37
lines changed

8 files changed

+90
-37
lines changed

frontend/components/DarkModeToggle.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React, { JSX, useEffect } from "react"
22
import { ComputerIcon, MoonIcon, SunIcon } from "./icons.js"
33
import { Button, ButtonProps, Tooltip } from "@heroui/react"
4+
import { tst } from "../utils/overrides.js"
45

56
export type DarkMode = "dark" | "light" | "system"
67

@@ -40,7 +41,7 @@ export function DarkModeToggle({ mode, onModeChange, className, ...rest }: MyCom
4041
<Tooltip content={`Toggle dark mode (currently ${mode})`}>
4142
<Button
4243
isIconOnly
43-
className={"mr-2 rounded-full bg-background hover:bg-default-100" + " " + className}
44+
className={`mr-2 rounded-full ${tst} bg-background hover:bg-default-100` + " " + className}
4445
aria-label="Toggle dark mode"
4546
onPress={() => {
4647
const curModeIdx = icons.findIndex(({ name }) => name === mode)

frontend/components/DecryptPaste.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { parseFilenameFromContentDisposition, parsePath } from "../../shared/par
1010
import { formatSize } from "../utils/utils.js"
1111
import { DarkMode, DarkModeToggle, defaultDarkMode, shouldBeDark } from "./DarkModeToggle.js"
1212
import binaryExtensions from "binary-extensions"
13+
import { tst } from "../utils/overrides.js"
1314

1415
function isBinaryPath(path: string) {
1516
return binaryExtensions.includes(path.replace(/.*\./, ""))
@@ -141,11 +142,11 @@ export function DecryptPaste() {
141142

142143
const showFileContent = pasteFile && (!isFileBinary || forceShowBinary)
143144

144-
const buttonClasses = "rounded-full bg-background hover:bg-default-100"
145+
const buttonClasses = `rounded-full bg-background hover:bg-default-100 ${tst}`
145146
return (
146147
<main
147148
className={
148-
"flex flex-col items-center min-h-screen transition-transform-background bg-background text-foreground w-full p-2" +
149+
`flex flex-col items-center min-h-screen transition-transform-background bg-background ${tst} text-foreground w-full p-2` +
149150
(shouldBeDark(darkModeSelect) ? " dark" : " light")
150151
}
151152
>
@@ -181,7 +182,7 @@ export function DecryptPaste() {
181182
<DarkModeToggle mode={darkModeSelect} onModeChange={setDarkModeSelect} className={buttonClasses} />
182183
</div>
183184
<div className="my-4">
184-
<div className="min-h-[30rem] w-full bg-secondary-50 rounded-lg p-3 relative">
185+
<div className={`min-h-[30rem] w-full bg-secondary-50 rounded-lg p-3 relative ${tst}`}>
185186
{isLoading ? (
186187
<CircularProgress className="absolute top-[50%] left-[50%] translate-[-50%]" />
187188
) : (

frontend/components/PasteBin.tsx

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import "../style.css"
2424
import { UploadedPanel } from "./UploadedPanel.js"
2525
import { PasteEditor, PasteEditState } from "./PasteEditor.js"
2626
import { uploadPaste } from "../utils/uploader.js"
27+
import { tst } from "../utils/overrides.js"
2728

2829
export function PasteBin() {
2930
const [editorState, setEditorState] = useState<PasteEditState>({
@@ -179,7 +180,10 @@ export function PasteBin() {
179180
<p className="my-2">An open source pastebin deployed on Cloudflare Workers. </p>
180181
<p className="my-2">
181182
<b>Usage</b>: Paste text or file here; submit; share it with a URL. (
182-
<Link href={`${BaseUrl}/api`}>API Documentation</Link>)
183+
<Link className={tst} href={`${BaseUrl}/api`}>
184+
API Documentation
185+
</Link>
186+
)
183187
</p>
184188
<p className="my-2">
185189
<b>Warning</b>: Only for temporary share <b>(max {maxExpirationReadable})</b>. Files could be deleted without
@@ -191,12 +195,12 @@ export function PasteBin() {
191195
const submitter = (
192196
<div className="my-4 mx-2 lg:mx-0">
193197
{/* eslint-disable-next-line @typescript-eslint/no-misused-promises */}
194-
<Button color="primary" onPress={onUploadPaste} className="mr-4" isDisabled={!canUpload() || isLoading}>
198+
<Button color="primary" onPress={onUploadPaste} className={`mr-4 ${tst}`} isDisabled={!canUpload() || isLoading}>
195199
{pasteSetting.uploadKind === "manage" ? "Update" : "Upload"}
196200
</Button>
197201
{pasteSetting.uploadKind === "manage" ? (
198202
// eslint-disable-next-line @typescript-eslint/no-misused-promises
199-
<Button color="danger" onPress={deletePaste} isDisabled={!canDelete()}>
203+
<Button color="danger" onPress={deletePaste} className={tst} isDisabled={!canDelete()}>
200204
Delete
201205
</Button>
202206
) : null}
@@ -206,11 +210,11 @@ export function PasteBin() {
206210
const footer = (
207211
<footer className="px-3 my-4 text-center">
208212
<p>
209-
<Link href={`${BaseUrl}/tos`} className="d-inline-block">
213+
<Link href={`${BaseUrl}/tos`} className={`d-inline-block ${tst}`}>
210214
Terms & Conditions
211215
</Link>
212216
{" / "}
213-
<Link href={REPO} className="d-inline-block">
217+
<Link href={REPO} className={`d-inline-block ${tst}`}>
214218
Repository
215219
</Link>
216220
</p>
@@ -220,7 +224,7 @@ export function PasteBin() {
220224
return (
221225
<main
222226
className={
223-
"flex flex-col items-center min-h-screen font-sans transition-transform-background bg-background text-foreground" +
227+
`flex flex-col items-center min-h-screen font-sans ${tst} bg-background text-foreground` +
224228
(shouldBeDark(darkModeSelect) ? " dark" : " light")
225229
}
226230
>
@@ -234,7 +238,7 @@ export function PasteBin() {
234238
/>
235239
<div className="flex flex-col items-start lg:flex-row gap-4 mx-2 lg:mx-0">
236240
<PanelSettingsPanel
237-
className={"transition-width ease-in-out lg:w-1/2 w-full"}
241+
className={"transition-width lg:w-1/2 w-full"}
238242
setting={pasteSetting}
239243
onSettingChange={setPasteSetting}
240244
/>

frontend/components/PasteEditor.tsx

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import { Card, CardBody, CardProps, Tab, Tabs, Textarea } from "@heroui/react"
1+
import { Card, CardBody, CardProps, mergeClasses, Tab, Tabs, Textarea } from "@heroui/react"
22
import React, { useRef, useState, DragEvent } from "react"
33
import { formatSize } from "../utils/utils.js"
44
import { XIcon } from "./icons.js"
5+
import { cardOverrides, textAreaOverrides, tst } from "../utils/overrides.js"
56

67
export type EditKind = "edit" | "file"
78

@@ -41,14 +42,14 @@ export function PasteEditor({ isPasteLoading, state, onStateChange, ...rest }: P
4142
}
4243

4344
return (
44-
<Card aria-label="Pastebin editor panel" {...rest}>
45+
<Card aria-label="Pastebin editor panel" classNames={cardOverrides} {...rest}>
4546
<CardBody>
4647
<Tabs
4748
variant="underlined"
4849
classNames={{
49-
tabList: "gap-2 w-full px-2 py-0 border-divider",
50-
cursor: "w-[80%]",
51-
tab: "max-w-fit px-2 h-8 px-2",
50+
tabList: `gap-2 w-full px-2 py-0 border-divider`,
51+
cursor: `w-[80%] ${tst}`,
52+
tab: `max-w-fit px-2 h-8 px-2`,
5253
panel: "pb-1",
5354
}}
5455
selectedKey={state.editKind}
@@ -62,9 +63,7 @@ export function PasteEditor({ isPasteLoading, state, onStateChange, ...rest }: P
6263
placeholder={isPasteLoading ? "Loading..." : "Edit your paste here"}
6364
isDisabled={isPasteLoading}
6465
className="px-0 py-0"
65-
classNames={{
66-
input: "resize-y min-h-[30em] font-mono",
67-
}}
66+
classNames={mergeClasses(textAreaOverrides, { input: "resize-y min-h-[30rem] font-mono" })}
6867
aria-label="Paste editor"
6968
disableAutosize
7069
disableAnimation
@@ -79,7 +78,7 @@ export function PasteEditor({ isPasteLoading, state, onStateChange, ...rest }: P
7978
<Tab key="file" title="File">
8079
<div
8180
className={
82-
"w-full h-[20rem] rounded-xl flex flex-col items-center justify-center cursor-pointer relative" +
81+
`w-full h-[20rem] rounded-xl flex flex-col items-center justify-center cursor-pointer relative ${tst}` +
8382
(isDragged ? " bg-primary-100" : " bg-primary-50")
8483
}
8584
role="button"
@@ -102,7 +101,7 @@ export function PasteEditor({ isPasteLoading, state, onStateChange, ...rest }: P
102101
}}
103102
/>
104103
<div className="text-2xl my-2 font-bold">Select File</div>
105-
<p className="text-1xl text-foreground-500 relative">
104+
<p className={`text-1xl text-foreground-500 ${tst} relative`}>
106105
<span>
107106
{state.file !== null
108107
? `${state.file.name} (${formatSize(state.file.size)})`
@@ -113,7 +112,7 @@ export function PasteEditor({ isPasteLoading, state, onStateChange, ...rest }: P
113112
<XIcon
114113
aria-label="Remove file"
115114
role="button"
116-
className="h-6 inline absolute top-2 right-2 text-red-400"
115+
className={`h-6 inline absolute top-2 right-2 text-red-400 ${tst}`}
117116
onClick={(e) => {
118117
e.stopPropagation()
119118
setFile(null)

frontend/components/PasteSettingPanel.tsx

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
import { BaseUrl, verifyExpiration, verifyManageUrl, verifyName } from "../utils/utils.js"
1414
import React from "react"
1515
import { InfoIcon } from "./icons.js"
16+
import { cardOverrides, inputOverrides, radioOverrides, switchOverrides, tst } from "../utils/overrides.js"
1617

1718
export type UploadKind = "short" | "long" | "custom" | "manage"
1819

@@ -33,17 +34,20 @@ interface PasteSettingPanelProps extends CardProps {
3334

3435
export function PanelSettingsPanel({ setting, onSettingChange, ...rest }: PasteSettingPanelProps) {
3536
return (
36-
<Card aria-label="Pastebin setting panel" {...rest}>
37+
<Card aria-label="Pastebin setting panel" classNames={cardOverrides} {...rest}>
3738
<CardHeader className="text-2xl">Settings</CardHeader>
38-
<Divider />
39+
<Divider className={tst} />
3940
<CardBody>
4041
<div className="gap-4 mb-4 flex flex-row">
4142
<Input
4243
type="text"
4344
label="Expiration"
4445
// to avoid duplicated name, see https://github.com/adobe/react-spectrum/discussions/8037
4546
aria-labelledby=""
46-
className="basis-80"
47+
classNames={{
48+
base: "basis-80",
49+
...inputOverrides,
50+
}}
4751
defaultValue="7d"
4852
value={setting.expiration}
4953
isRequired
@@ -58,6 +62,7 @@ export function PanelSettingsPanel({ setting, onSettingChange, ...rest }: PasteS
5862
aria-labelledby=""
5963
value={setting.password}
6064
onValueChange={(p) => onSettingChange({ ...setting, password: p })}
65+
classNames={inputOverrides}
6166
placeholder={"Generated randomly"}
6267
description="Used to update/delete your paste"
6368
/>
@@ -67,27 +72,28 @@ export function PanelSettingsPanel({ setting, onSettingChange, ...rest }: PasteS
6772
value={setting.uploadKind}
6873
onValueChange={(v) => onSettingChange({ ...setting, uploadKind: v as UploadKind })}
6974
>
70-
<Radio value="short" description={`Example: ${BaseUrl}/BxWH`}>
75+
<Radio value="short" description={`Example: ${BaseUrl}/BxWH`} classNames={radioOverrides}>
7176
Generate a short random URL
7277
</Radio>
7378
<Radio
7479
value="long"
7580
description={`Example: ${BaseUrl}/5HQWYNmjA4h44SmybeThXXAm`}
7681
classNames={{
7782
description: "text-ellipsis max-w-[calc(100vw-5rem)] whitespace-nowrap overflow-hidden",
83+
...radioOverrides,
7884
}}
7985
>
8086
Generate a long random URL
8187
</Radio>
82-
<Radio value="custom" description={`Example: ${BaseUrl}/~stocking`}>
88+
<Radio value="custom" classNames={radioOverrides} description={`Example: ${BaseUrl}/~stocking`}>
8389
Set by your own
8490
</Radio>
8591
{setting.uploadKind === "custom" ? (
8692
<Input
8793
value={setting.name}
8894
onValueChange={(n) => onSettingChange({ ...setting, name: n })}
8995
type="text"
90-
className="shrink"
96+
classNames={inputOverrides}
9197
isInvalid={!verifyName(setting.name)[0]}
9298
errorMessage={verifyName(setting.name)[1]}
9399
startContent={
@@ -97,7 +103,7 @@ export function PanelSettingsPanel({ setting, onSettingChange, ...rest }: PasteS
97103
}
98104
/>
99105
) : null}
100-
<Radio value="manage">
106+
<Radio value="manage" classNames={radioOverrides}>
101107
<div className="">Update or delete</div>
102108
</Radio>
103109
{setting.uploadKind === "manage" ? (
@@ -112,9 +118,13 @@ export function PanelSettingsPanel({ setting, onSettingChange, ...rest }: PasteS
112118
/>
113119
) : null}
114120
</RadioGroup>
115-
<Divider />
121+
<Divider className={tst} />
116122
<div className="mt-3 flex flex-row items-center">
117-
<Switch isSelected={setting.doEncrypt} onValueChange={(v) => onSettingChange({ ...setting, doEncrypt: v })}>
123+
<Switch
124+
classNames={switchOverrides}
125+
isSelected={setting.doEncrypt}
126+
onValueChange={(v) => onSettingChange({ ...setting, doEncrypt: v })}
127+
>
118128
Client-side encryption
119129
</Switch>
120130
<Tooltip

frontend/components/UploadedPanel.tsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import { Card, CardBody, CardHeader, CardProps, Divider, Skeleton, Snippet } from "@heroui/react"
1+
import { Card, CardBody, CardHeader, CardProps, Divider, mergeClasses, Skeleton, Snippet } from "@heroui/react"
22
import React from "react"
33
import { PasteResponse } from "../../shared/interfaces.js"
4+
import { tst } from "../utils/overrides.js"
45

56
interface UploadedPanelProps extends CardProps {
67
isLoading: boolean
@@ -14,15 +15,15 @@ const makeDecryptionUrl = (url: string, key: string) => {
1415
return urlParsed.toString() + "#" + key
1516
}
1617

17-
export function UploadedPanel({ isLoading, pasteResponse, encryptionKey, ...rest }: UploadedPanelProps) {
18+
export function UploadedPanel({ isLoading, pasteResponse, className, encryptionKey, ...rest }: UploadedPanelProps) {
1819
const snippetClassNames = {
19-
pre: "overflow-scroll leading-[2.5] font-sans",
20-
base: "w-full py-1/3",
21-
copyButton: "relative ml-[-12pt] left-[5pt]",
20+
pre: `overflow-scroll leading-[2.5] font-sans ${tst}`,
21+
base: `w-full py-1/3 ${tst}`,
22+
copyButton: `relative ml-[-12pt] left-[5pt] ${tst}`,
2223
}
2324
const firstColClassNames = "w-[8rem] mr-4 whitespace-nowrap"
2425
return (
25-
<Card {...rest}>
26+
<Card classNames={mergeClasses({ base: tst }, { base: className })} {...rest}>
2627
<CardHeader className="text-2xl">Uploaded Paste</CardHeader>
2728
<Divider />
2829
<CardBody>

frontend/style.css

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,15 @@
1111
--font-mono:
1212
"Cascadia Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
1313
}
14+
15+
.color-tst {
16+
transition-property: background-color, color, border-color;
17+
transition-duration: var(--default-transition-duration);
18+
}
19+
20+
@layer utilities {
21+
.\!color-tst {
22+
transition-property: background-color, color, border-color;
23+
transition-duration: var(--default-transition-duration) !important;
24+
}
25+
}

frontend/utils/overrides.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import type { CardSlots, InputSlots, RadioSlots, SlotsToClasses, ToggleSlots } from "@heroui/react"
2+
3+
export const tst = "color-tst"
4+
5+
export const inputOverrides: SlotsToClasses<InputSlots> = {
6+
inputWrapper: `!${tst}`,
7+
input: tst,
8+
}
9+
10+
export const radioOverrides: SlotsToClasses<RadioSlots> = {
11+
control: tst,
12+
label: tst,
13+
labelWrapper: tst,
14+
wrapper: tst,
15+
}
16+
17+
export const switchOverrides: SlotsToClasses<ToggleSlots> = {
18+
wrapper: tst,
19+
}
20+
21+
export const cardOverrides: SlotsToClasses<CardSlots> = { base: tst }
22+
23+
export const textAreaOverrides: SlotsToClasses<InputSlots> = {
24+
inputWrapper: tst,
25+
}

0 commit comments

Comments
 (0)