diff --git a/src/_labs/Pronouns/Pronouns.story.tsx b/src/_labs/Pronouns/Pronouns.story.tsx index 8dfe85501..9a4d213d0 100644 --- a/src/_labs/Pronouns/Pronouns.story.tsx +++ b/src/_labs/Pronouns/Pronouns.story.tsx @@ -10,7 +10,14 @@ import { AutoplayList } from './AutoplayList'; import { Header } from '../../typography'; -export default { title: 'labs/Pronouns' }; +export default { + title: 'labs/Pronouns', + parameters: { + lostpixel: { + disable: true, + }, + }, +}; const componentName = 'Comment'; diff --git a/src/_labs/ThemeKit/ThemeKit.story.tsx b/src/_labs/ThemeKit/ThemeKit.story.tsx index 1d87995a6..ede3a77b2 100644 --- a/src/_labs/ThemeKit/ThemeKit.story.tsx +++ b/src/_labs/ThemeKit/ThemeKit.story.tsx @@ -23,7 +23,14 @@ import { Header as H, Paragraph as P } from '../../typography'; import { LoaderCircular as LC } from '../../motion'; import { Gear } from '../../icons'; -export default { title: 'Labs/Themekit' }; +export default { + title: 'Labs/Themekit', + parameters: { + lostpixel: { + disable: true, + }, + }, +}; export function Common() { return ; diff --git a/src/utils/hooks/useCharacterCount/useCharacterCount.state.ts b/src/utils/hooks/useCharacterCount/useCharacterCount.state.ts new file mode 100644 index 000000000..a99010527 --- /dev/null +++ b/src/utils/hooks/useCharacterCount/useCharacterCount.state.ts @@ -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, + }; + } +} diff --git a/src/utils/hooks/useCharacterCount/useCharacterCount.ts b/src/utils/hooks/useCharacterCount/useCharacterCount.ts new file mode 100644 index 000000000..1929999a1 --- /dev/null +++ b/src/utils/hooks/useCharacterCount/useCharacterCount.ts @@ -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; + 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, + }; +} diff --git a/src/utils/index.ts b/src/utils/index.ts index e4cc72573..32e69cc8a 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -28,6 +28,7 @@ export { usePortal_DEPRECATED, validate, ANCHOR_POINTS } from './hooks/usePortal export { useStateTransmorphic } from './hooks/useStateTransmorphic'; export { useStyleVars } from './hooks/useStyleVars'; export { withIris } from './HOCs/withIris'; +export { useCharacterCount } from './hooks/useCharacterCount/useCharacterCount'; export type { Attach, AttachAlias, SimpleAnimation } from './hooks/usePortal_DEPRECATED'; export type { onClose } from './events/onClose';