-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
1fe4fd9
commit ff10168
Showing
7 changed files
with
240 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
# flushSync |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
main { | ||
display: flex; | ||
flex-direction: column; | ||
gap: 1rem; | ||
padding: 3rem; | ||
} | ||
.editable-text { | ||
button { | ||
/* remove button styles. Make it look like text */ | ||
background: none; | ||
border: none; | ||
padding: 4px 8px; | ||
font-size: 1.5rem; | ||
font-weight: bold; | ||
} | ||
|
||
input { | ||
/* make it the same size as the button */ | ||
font-size: 1.5rem; | ||
font-weight: bold; | ||
padding: 4px 8px; | ||
border: none; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import { useRef, useState } from 'react' | ||
import * as ReactDOM from 'react-dom/client' | ||
|
||
function EditableText({ | ||
id, | ||
initialValue = '', | ||
fieldName, | ||
inputLabel, | ||
buttonLabel, | ||
}: { | ||
id?: string | ||
initialValue?: string | ||
fieldName: string | ||
inputLabel: string | ||
buttonLabel: string | ||
}) { | ||
const [edit, setEdit] = useState(false) | ||
const [value, setValue] = useState(initialValue) | ||
const inputRef = useRef<HTMLInputElement>(null) | ||
// 🐨 add a button ref here | ||
|
||
return edit ? ( | ||
<form | ||
method="post" | ||
onSubmit={event => { | ||
event.preventDefault() | ||
// here's where you'd send the updated value to the server | ||
// 🐨 wrap these calls in a flushSync | ||
setValue(inputRef.current?.value ?? '') | ||
setEdit(false) | ||
// 🐨 after flushSync, focus the button with the button ref | ||
}} | ||
> | ||
<input | ||
required | ||
ref={inputRef} | ||
type="text" | ||
id={id} | ||
aria-label={inputLabel} | ||
name={fieldName} | ||
defaultValue={value} | ||
onKeyDown={event => { | ||
if (event.key === 'Escape') { | ||
// 🐨 wrap this in a flushSync | ||
setEdit(false) | ||
// 🐨 after the flushSync, focus the button | ||
} | ||
}} | ||
onBlur={event => { | ||
// 🐨 wrap these in a flushSync | ||
setValue(event.currentTarget.value) | ||
setEdit(false) | ||
// 🐨 after the flushSync, focus the button | ||
}} | ||
/> | ||
</form> | ||
) : ( | ||
<button | ||
aria-label={buttonLabel} | ||
// 🐨 add a ref prop for the button | ||
type="button" | ||
onClick={() => { | ||
// 🐨 wrap this in a flushSync | ||
setEdit(true) | ||
// 🐨 after the flushSync, select all the text of the input | ||
}} | ||
> | ||
{value || 'Edit'} | ||
</button> | ||
) | ||
} | ||
|
||
function App() { | ||
return ( | ||
<main> | ||
<button>Focus before</button> | ||
<div className="editable-text"> | ||
<EditableText | ||
initialValue="Unnamed" | ||
fieldName="name" | ||
inputLabel="Edit project name" | ||
buttonLabel="Edit project name" | ||
/> | ||
</div> | ||
<button>Focus after</button> | ||
</main> | ||
) | ||
} | ||
|
||
const rootEl = document.createElement('div') | ||
document.body.append(rootEl) | ||
ReactDOM.createRoot(rootEl).render(<App />) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
# flushSync |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
main { | ||
display: flex; | ||
flex-direction: column; | ||
gap: 1rem; | ||
padding: 3rem; | ||
} | ||
.editable-text { | ||
button { | ||
/* remove button styles. Make it look like text */ | ||
background: none; | ||
border: none; | ||
padding: 4px 8px; | ||
font-size: 1.5rem; | ||
font-weight: bold; | ||
} | ||
|
||
input { | ||
/* make it the same size as the button */ | ||
font-size: 1.5rem; | ||
font-weight: bold; | ||
padding: 4px 8px; | ||
border: none; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
import { useRef, useState } from 'react' | ||
import { flushSync } from 'react-dom' | ||
import * as ReactDOM from 'react-dom/client' | ||
|
||
function EditableText({ | ||
id, | ||
initialValue = '', | ||
fieldName, | ||
inputLabel, | ||
buttonLabel, | ||
}: { | ||
id?: string | ||
initialValue?: string | ||
fieldName: string | ||
inputLabel: string | ||
buttonLabel: string | ||
}) { | ||
const [edit, setEdit] = useState(false) | ||
const [value, setValue] = useState(initialValue) | ||
const inputRef = useRef<HTMLInputElement>(null) | ||
const buttonRef = useRef<HTMLButtonElement>(null) | ||
|
||
return edit ? ( | ||
<form | ||
method="post" | ||
onSubmit={event => { | ||
event.preventDefault() | ||
// here's where you'd send the updated value to the server | ||
flushSync(() => { | ||
setValue(inputRef.current?.value ?? '') | ||
setEdit(false) | ||
}) | ||
buttonRef.current?.focus() | ||
}} | ||
> | ||
<input | ||
required | ||
ref={inputRef} | ||
type="text" | ||
id={id} | ||
aria-label={inputLabel} | ||
name={fieldName} | ||
defaultValue={value} | ||
onKeyDown={event => { | ||
if (event.key === 'Escape') { | ||
flushSync(() => { | ||
setEdit(false) | ||
}) | ||
buttonRef.current?.focus() | ||
} | ||
}} | ||
onBlur={event => { | ||
setValue(event.currentTarget.value) | ||
setEdit(false) | ||
}} | ||
/> | ||
</form> | ||
) : ( | ||
<button | ||
aria-label={buttonLabel} | ||
type="button" | ||
ref={buttonRef} | ||
onClick={() => { | ||
flushSync(() => { | ||
setEdit(true) | ||
}) | ||
inputRef.current?.select() | ||
}} | ||
> | ||
{value || 'Edit'} | ||
</button> | ||
) | ||
} | ||
|
||
function App() { | ||
return ( | ||
<main> | ||
<button>Focus before</button> | ||
<div className="editable-text"> | ||
<EditableText | ||
initialValue="Unnamed" | ||
fieldName="name" | ||
inputLabel="Edit project name" | ||
buttonLabel="Edit project name" | ||
/> | ||
</div> | ||
<button>Focus after</button> | ||
</main> | ||
) | ||
} | ||
|
||
const rootEl = document.createElement('div') | ||
document.body.append(rootEl) | ||
ReactDOM.createRoot(rootEl).render(<App />) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# Focus Management | ||
|
||
This example was uses code from | ||
[trellix](https://github.com/remix-run/example-trellix/blob/3379b3d5e9c0173381031e4f062877e8a3696b2e/app/routes/board.%24id/components.tsx). |