Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save neg4n/f530f1d35a2bf0188142d47b52b8378d to your computer and use it in GitHub Desktop.
Save neg4n/f530f1d35a2bf0188142d47b52b8378d to your computer and use it in GitHub Desktop.
Chakra-ui editable textarea
import {
chakra,
forwardRef,
omitThemingProps,
StylesProvider,
SystemStyleObject,
ThemingProps,
useMultiStyleConfig,
useStyles,
HTMLChakraProps,
} from '@chakra-ui/system'
import { cx, runIfFn, __DEV__ } from '@chakra-ui/utils'
import { createContext, MaybeRenderProp } from '@chakra-ui/react-utils'
import * as React from 'react'
import { useEditable, UseEditableProps, UseEditableReturn } from '@chakra-ui/react'
type EditableContext = Omit<UseEditableReturn, 'htmlProps'>
const [EditableProvider, useEditableContext] = createContext<EditableContext>({
name: 'EditableContext',
errorMessage:
'useEditableContext: context is undefined. Seems you forgot to wrap the editable components in `<Editable />`',
})
type RenderProps = Pick<UseEditableReturn, 'isEditing' | 'onSubmit' | 'onCancel' | 'onEdit'>
interface BaseEditableProps
extends Omit<HTMLChakraProps<'div'>, 'onChange' | 'value' | 'defaultValue' | 'onSubmit'> {}
export interface EditableProps
extends UseEditableProps,
BaseEditableProps,
ThemingProps<'Editable'> {
children?: MaybeRenderProp<RenderProps>
}
/**
* Editable
*
* The wrapper that provides context and logic for all editable
* components. It renders a `div`
*/
export const Editable = forwardRef<EditableProps, 'div'>((props, ref) => {
const styles = useMultiStyleConfig('Editable', props)
const ownProps = omitThemingProps(props)
const { htmlProps, ...context } = useEditable(ownProps)
const { isEditing, onSubmit, onCancel, onEdit } = context
const _className = cx('chakra-editable', props.className)
const children = runIfFn(props.children, {
isEditing,
onSubmit,
onCancel,
onEdit,
})
return (
<EditableProvider value={context}>
<StylesProvider value={styles}>
<chakra.div ref={ref} {...(htmlProps as HTMLChakraProps<'div'>)} className={_className}>
{children}
</chakra.div>
</StylesProvider>
</EditableProvider>
)
})
if (__DEV__) {
Editable.displayName = 'Editable'
}
const commonStyles: SystemStyleObject = {
fontSize: 'inherit',
fontWeight: 'inherit',
textAlign: 'inherit',
bg: 'transparent',
}
export interface EditablePreviewProps extends HTMLChakraProps<'div'> {}
/**
* EditablePreview
*
* The `span` used to display the final value, in the `preview` mode
*/
export const EditablePreview = forwardRef<EditablePreviewProps, 'span'>((props, ref) => {
const { getPreviewProps } = useEditableContext()
const styles = useStyles()
const previewProps = getPreviewProps(props, ref) as HTMLChakraProps<'span'>
const _className = cx('chakra-editable__preview', props.className)
return (
<chakra.span
{...previewProps}
__css={{
cursor: 'text',
display: 'inline-block',
...commonStyles,
...styles.preview,
}}
className={_className}
/>
)
})
if (__DEV__) {
EditablePreview.displayName = 'EditablePreview'
}
export interface EditableInputProps extends HTMLChakraProps<'input'> {}
/**
* EditableInput
*
* The input used in the `edit` mode
*/
export const EditableTextarea = forwardRef<EditableInputProps, 'textarea'>((props, ref) => {
const { getInputProps } = useEditableContext()
const styles = useStyles()
const textareaProps = getInputProps(props, ref) as HTMLChakraProps<'textarea'>
const _className = cx('chakra-editable__textarea', props.className)
return (
<chakra.textarea
{...textareaProps}
__css={{
outline: 0,
...commonStyles,
...styles.input,
}}
className={_className}
/>
)
})
if (__DEV__) {
EditableTextarea.displayName = 'EditableTextarea'
}
/**
* React hook use to gain access to the editable state and actions.
*/
export function useEditableState() {
const { isEditing, onSubmit, onCancel, onEdit, isDisabled } = useEditableContext()
return {
isEditing,
onSubmit,
onCancel,
onEdit,
isDisabled,
}
}
/**
* React hook use to create controls for the editable component
*/
export function useEditableControls(): Pick<
EditableContext,
'isEditing' | 'getEditButtonProps' | 'getCancelButtonProps' | 'getSubmitButtonProps'
> {
const { isEditing, getEditButtonProps, getCancelButtonProps, getSubmitButtonProps } =
useEditableContext()
return {
isEditing,
getEditButtonProps,
getCancelButtonProps,
getSubmitButtonProps,
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment