imaginerio/narratives

View on GitHub
src/components/Layers/index.jsx

Summary

Maintainability
A
0 mins
Test Coverage
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import { useQuery, useMutation, gql } from '@apollo/client';
import { some, pick } from 'lodash';
import { Segment, Form, Button, Icon } from 'semantic-ui-react';
import { TbShape, TbShape2 } from 'react-icons/tb';

import Basemaps from '../Basemaps';
import useLocale from '../../hooks/useLocale';

import styles from './Layers.module.css';

const GET_SLIDE = gql`
  query GetSlide($slide: ID!) {
    Slide(where: { id: $slide }) {
      id
      disabledLayers: layers {
        id
        layerId
      }
    }
  }
`;

const GET_LAYERS = gql`
  query {
    layers: allLayers(orderBy: "title_ASC") {
      id
      layerId
      title
      remoteId
    }
  }
`;

const UPDATE_LAYERS = gql`
  mutation UpdateLayers($slide: ID!, $layers: LayerRelateToManyInput) {
    updateSlide(id: $slide, data: { layers: $layers }) {
      id
      layers {
        id
        layerId
      }
    }
  }
`;

const Layers = ({ slide }) => {
  const { data } = useQuery(GET_SLIDE, {
    variables: { slide },
  });
  const allLayers = useQuery(GET_LAYERS);
  const [updateLayers] = useMutation(UPDATE_LAYERS);

  const onLayersChange = newLayers => {
    updateLayers({
      variables: {
        slide,
        layers: {
          connect: newLayers.map(nl => ({ id: nl.id })),
          disconnectAll: true,
        },
      },
      optimisticResponse: {
        __typename: 'Mutation',
        updateSlide: {
          __typename: 'Slide',
          id: slide,
          layers: newLayers.map(l => ({
            __typename: 'Layer',
            ...pick(l, 'id', 'layerId'),
          })),
        },
      },
    });
  };

  const [disabledLayers, setDisabledLayers] = useState([]);
  const [open, setOpen] = useState(false);

  const { layers } = useLocale();

  useEffect(() => {
    if (data) {
      setDisabledLayers(data.Slide.disabledLayers);
    }
  }, [data]);

  useEffect(() => {
    onLayersChange(disabledLayers);
  }, [disabledLayers]);

  return (
    <>
      <Button
        icon="clone outline"
        onClick={() => setOpen(!open)}
        style={{ display: 'inline-block' }}
      />
      {open && (
        <Segment className={styles.layerMenu}>
          <Icon
            style={{ float: 'right', cursor: 'pointer' }}
            name="close"
            onClick={() => setOpen(false)}
          />
          <Basemaps slide={slide} />
          <Form.Group style={{ borderTop: '1px solid #ccc', marginTop: 15, paddingTop: 15 }}>
            <h3 style={{ marginTop: 0 }}>{layers}</h3>
            {allLayers.data.layers
              .filter(l => l.title)
              .sort((a, b) => a.title.localeCompare(b.title))
              .map(layer => (
                <Form.Field
                  className={styles.layerCheck}
                  key={layer.id}
                  // eslint-disable-next-line prettier/prettier
                  label={(
                    <div>
                      {layer.layerId.match(/poly/gi) ? <TbShape /> : <TbShape2 />}
                      <span>{layer.title}</span>
                    </div>
                    // eslint-disable-next-line prettier/prettier
                  )}
                  control="input"
                  type="checkbox"
                  value={layer.id}
                  checked={!some(disabledLayers, nl => nl.id === layer.id)}
                  onChange={e => {
                    const { value, checked } = e.target;
                    if (checked) {
                      return setDisabledLayers(disabledLayers.filter(nl => nl.id !== value));
                    }
                    return setDisabledLayers([
                      ...disabledLayers,
                      allLayers.data.layers.find(l => l.id === value),
                    ]);
                  }}
                />
              ))}
          </Form.Group>
        </Segment>
      )}
    </>
  );
};

Layers.propTypes = {
  slide: PropTypes.string.isRequired,
};

export default Layers;