start-ui-web icon indicating copy to clipboard operation
start-ui-web copied to clipboard

Code to implement : Editable component

Open FabienEssid opened this issue 4 years ago β€’ 1 comments
trafficstars

I suggest you my whole component : Editable I think we will need to make some adjustments like using Typescript. Here is the entire code :

Editable.js
import React, { Fragment, useState } from 'react';

import {
  ButtonGroup,
  Flex,
  IconButton,
  Text,
  Textarea,
} from '@chakra-ui/react';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import { HiCheck, HiPencilAlt, HiX } from 'react-icons/hi';
import TextareaAutosize from 'react-textarea-autosize';

export const Editable = ({
  value,
  onSubmit,
  onChange,
  onCancel,
  isSubmitDisabled,
  ...rest
}) => {
  const { t } = useTranslation();

  const [isEditing, setIsEditing] = useState(false);
  const [content, setContent] = useState(value);

  const handleEdit = () => {
    if (isEditing) {
      setIsEditing(false);
      onSubmit(content.trim());
      return;
    }

    setIsEditing(true);
    setContent(value);
  };

  const handleCancel = () => {
    setContent(value);
    setIsEditing((x) => !x);
    onCancel(value);
  };

  const handleChange = (e) => {
    setContent(e.target?.value);

    onChange(e);
  };

  return (
    <Flex alignItems="flex-start" {...rest}>
      {isEditing ? (
        <Textarea
          as={TextareaAutosize}
          value={content}
          transition="none"
          ml="-0.5rem"
          mr={3}
          px={2}
          py={1}
          minH={4}
          autoFocus
          maxRows="10"
          onChange={handleChange}
        />
      ) : (
        <Text
          flexGrow={1}
          color="gray.500"
          lineHeight="1.375" // set this lineHeight to be the same as textarea default lineHeight
          py={1}
          mr={2}
        >
          {(value || '').split('\n').map((item, key) => (
            <Fragment key={key}>
              {item}
              <br />
            </Fragment>
          ))}
        </Text>
      )}
      <ButtonGroup size="sm">
        {isEditing && (
          <IconButton
            aria-label={t('components.editable.cancel')}
            onClick={handleCancel}
            icon={<HiX />}
          />
        )}
        <IconButton
          aria-label={
            isEditing
              ? t('components.editable.submit')
              : t('components.editable.edit')
          }
          isDisabled={isSubmitDisabled}
          onClick={handleEdit}
          icon={isEditing ? <HiCheck /> : <HiPencilAlt />}
        />
      </ButtonGroup>
    </Flex>
  );
};

Editable.propTypes = {
  value: PropTypes.string,
  onSubmit: PropTypes.func,
  onChange: PropTypes.func,
  onCancel: PropTypes.func,
  isSubmitDisabled: PropTypes.bool,
};

Editable.defaultProps = {
  value: '',
  onSubmit: () => {},
  onChange: () => {},
  onCancel: () => {},
  isSubmitDisabled: false,
};

Editable.stories.mdx
import { Editable } from './Editable';

export default {
  title: 'components/Editable',
  component: Editable,
};

export const Default = () => <Editable />;

export const UsageWithValue = () => <Editable value="One Prepaid" />;

export const UsageWithTriggeredEvents = () => {
  const handleCancel = (value) => {
    console.log('Cancel', { value });
  };

  const handleSubmit = (value) => {
    console.log('Submit', { value });
  };

  const handleChange = (e) => {
    console.log('Change', { value: e.target.value });
  };

  return (
    <Editable
      onCancel={handleCancel}
      onSubmit={handleSubmit}
      onChange={handleChange}
    />
  );
};

Result example :
https://user-images.githubusercontent.com/50022361/106932147-aaa85780-6717-11eb-8efc-52e68361588f.mp4

FabienEssid avatar Feb 04 '21 17:02 FabienEssid

A good example using zagjs https://zagjs.com/components/react/editable

yoannfleurydev avatar Jun 21 '22 16:06 yoannfleurydev