This repository has been archived by the owner on Oct 23, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add useCharacterCount to create controlled components
- Loading branch information
1 parent
c2333cd
commit 9196cc3
Showing
4 changed files
with
149 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,28 @@ | ||
import styled from 'styled-components'; | ||
|
||
import { red, yellow } from '../../../color'; | ||
import { Paragraph } from '../../../typography'; | ||
|
||
interface MessageType { | ||
warning: boolean; | ||
error: boolean; | ||
} | ||
|
||
interface Attrs { | ||
theme: { name: string }; | ||
} | ||
|
||
function attrs({ theme }: Attrs) { | ||
return { | ||
size: 3, | ||
format: theme.name === 'dark' ? 'soft' : 'alternative', | ||
}; | ||
} | ||
|
||
export const Counter = styled(Paragraph).attrs(attrs)<MessageType>` | ||
margin-top: 0.25rem; | ||
margin-bottom: 0; | ||
${(p) => p.warning && { fontWeight: 800, color: yellow(600) }}; | ||
${(p) => p.error && { fontWeight: 600, color: red(500) }}; | ||
`; |
42 changes: 42 additions & 0 deletions
42
src/utils/hooks/useCharacterCount/useCharacterCount.state.ts
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,42 @@ | ||
export interface CharacterCountState { | ||
error?: boolean; | ||
warning?: boolean; | ||
remainingCharacters?: number; | ||
} | ||
|
||
export type UserAction = | ||
| { type: 'SET_ERROR'; payload?: undefined } | ||
| { type: 'SET_WARNING'; payload?: undefined } | ||
| { type: 'RESET_STATUS'; payload?: undefined } | ||
| { type: 'SET_REMAINING_CHARACTERS'; payload: number }; | ||
|
||
export function reducer( | ||
state: CharacterCountState, | ||
{ type, payload }: UserAction | ||
): CharacterCountState { | ||
switch (type) { | ||
case 'SET_ERROR': | ||
return { | ||
...state, | ||
error: true, | ||
warning: false, | ||
}; | ||
case 'SET_WARNING': | ||
return { | ||
...state, | ||
warning: true, | ||
error: false, | ||
}; | ||
case 'RESET_STATUS': | ||
return { | ||
...state, | ||
warning: false, | ||
error: false, | ||
}; | ||
case 'SET_REMAINING_CHARACTERS': | ||
return { | ||
...state, | ||
remainingCharacters: payload, | ||
}; | ||
} | ||
} |
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,77 @@ | ||
import { useReducer, useCallback } from 'react'; | ||
|
||
import { | ||
reducer, | ||
CharacterCountState, | ||
UserAction, | ||
} from './useCharacterCount.state'; | ||
|
||
export interface UseCharacterCountInit { | ||
value?: string; | ||
/** Max number of characters allowed */ | ||
maxCharacters?: number; | ||
/** Shows warning color when threshold is met */ | ||
warningThreshold?: number; | ||
} | ||
|
||
export interface UseCharacterCount { | ||
state: CharacterCountState; | ||
dispatch: React.Dispatch<UserAction>; | ||
handleChange: (value: string) => void; | ||
clean: () => void; | ||
} | ||
|
||
/** | ||
* Based on withCharacterCount, this provides handlers for a character count message to create a controlled component. | ||
* Helpful to use in conjuction with custom inputs that need more control of input messages | ||
*/ | ||
|
||
export function useCharacterCount({ | ||
maxCharacters = 10, | ||
warningThreshold = 5, | ||
}: UseCharacterCountInit): UseCharacterCount { | ||
const [state, dispatch] = useReducer(reducer, { | ||
remainingCharacters: maxCharacters, | ||
}); | ||
|
||
function handleError() { | ||
dispatch({ | ||
type: 'SET_ERROR', | ||
}); | ||
} | ||
|
||
function handleWarn() { | ||
dispatch({ | ||
type: 'SET_WARNING', | ||
}); | ||
} | ||
|
||
const clean = useCallback(() => { | ||
dispatch({ | ||
type: 'RESET_STATUS', | ||
}); | ||
}, []); | ||
|
||
const handleChange = useCallback( | ||
(value: string) => { | ||
const remaining = maxCharacters - value.length; | ||
if (remaining <= warningThreshold && remaining > 0) | ||
handleWarn(); | ||
else if (remaining <= 0) handleError(); | ||
else clean(); | ||
|
||
dispatch({ | ||
type: 'SET_REMAINING_CHARACTERS', | ||
payload: remaining, | ||
}); | ||
}, | ||
[clean, maxCharacters, warningThreshold] | ||
); | ||
|
||
return { | ||
state, | ||
dispatch, | ||
handleChange, | ||
clean, | ||
}; | ||
} |
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