Skip to content
This repository has been archived by the owner on Oct 23, 2024. It is now read-only.

Commit

Permalink
Add useCharacterCount to create controlled components
Browse files Browse the repository at this point in the history
  • Loading branch information
juliewongbandue committed Oct 10, 2023
1 parent c2333cd commit 9196cc3
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 0 deletions.
28 changes: 28 additions & 0 deletions src/utils/hooks/useCharacterCount/Counter.tsx
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 src/utils/hooks/useCharacterCount/useCharacterCount.state.ts
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,
};
}
}
77 changes: 77 additions & 0 deletions src/utils/hooks/useCharacterCount/useCharacterCount.ts
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,
};
}
2 changes: 2 additions & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ 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 { Counter } from './hooks/useCharacterCount/Counter';

export type { Attach, AttachAlias, SimpleAnimation } from './hooks/usePortal_DEPRECATED';
export type { onClose } from './events/onClose';
Expand Down

0 comments on commit 9196cc3

Please sign in to comment.