Commit d21e364c authored by Yahya Al Hamad's avatar Yahya Al Hamad Committed by Yahya Al Hamad
Browse files

Issue #3256238 by YahyaAlHamad: Add a plugin to wrap blocks and act like the module "Field Groups"

parent 5c64f459
Loading
Loading
Loading
Loading

.gitignore

0 → 100644
+1 −0
Original line number Diff line number Diff line
node_modules
 No newline at end of file
+38 −0

File added.

Preview size limit exceeded, changes collapsed.

+47 −0
Original line number Diff line number Diff line
import React, { useEffect, useState } from "react";
import $ from "jquery";
import RegionInput from "./RegionInput";
import { Group } from "./styled";
const App = ({ blocks = {}, regions = {}, defaultValue = null }) => {
  const [value, setValue] = useState(defaultValue ? defaultValue : {});

  const handleRegionChange = (regionId, groups) => {
    const newValue = { ...value };
    newValue[regionId] = groups;
    setValue(newValue);
  };

  useEffect(() => {
    try {
      $("#blocks-wrapper-input").val(JSON.stringify(value));
    } catch (e) {
      console.error(e);
      $("#blocks-wrapper-input").val(JSON.stringify({}));
    }
  }, [value]);

  const defaultValueOfRegion = (regionId) => {
    if (defaultValue && defaultValue[regionId]) {
      return defaultValue[regionId];
    }

    return null;
  };

  return (
    <Group>
      {$.map(regions, (label, regionId) => (
        <RegionInput
          key={regionId}
          onChange={handleRegionChange}
          regionId={regionId}
          regionLabel={label}
          blocks={blocks}
          defaultValue={defaultValueOfRegion(regionId)}
        />
      ))}
    </Group>
  );
};

export default App;
+143 −0
Original line number Diff line number Diff line
import React, { useEffect, useState } from "react";
import SimpleAccordion from "./SimpleAccordion";
import $ from "jquery";
import {
  Group,
  Input,
  Label,
  Select,
  Option,
  GroupsWrapper,
  Button,
} from "./styled";
const RegionInput = ({
  regionId,
  regionLabel,
  blocks = [],
  onChange,
  defaultValue,
}) => {
  const [groups, setGroups] = useState([]);
  const [counter, setCounter] = useState(1);

  useEffect(() => {
    onChange(regionId, groups);
  }, [groups]);

  useEffect(() => {
    if (defaultValue) {
      setGroups(defaultValue);
    }
  }, [defaultValue]);

  const addGroup = (e) => {
    e.preventDefault();
    const newGroups = groups.map((group) => ({ ...group }));
    newGroups.push({
      blocks: [],
      wrapper: "div",
      attributes: "",
      admin_label: null,
      id: counter,
    });
    setCounter(counter + 1);
    setGroups(newGroups);
  };

  const removeGroup = (id) => {
    let newGroups = groups.map((group) => ({ ...group }));
    newGroups = newGroups.filter((group) => id !== group.id);
    setGroups(newGroups);
  };

  const handleChange = (groupId, e) => {
    let newGroups = groups.map((group) => ({ ...group }));
    const { name, value, selectedOptions, type } = e.target;
    const groupToEdit = newGroups.find((group) => group.id === groupId);
    if (type === "select-multiple") {
      let value = Array.from(selectedOptions, (option) => option.value);
      groupToEdit[name] = value;
    } else {
      groupToEdit[name] = value;
    }
    setGroups(newGroups);
  };

  return (
    <div className="blocks-wrapper-region">
      <SimpleAccordion label={regionLabel} classNames="regions">
        <GroupsWrapper>
          {groups.map((group) => (
            <SimpleAccordion
              key={group.id}
              label={
                group.admin_label ? group.admin_label : `Groups ${group.id}`
              }
              classNames="group"
              id={group.id}
              cancel
              onCancel={removeGroup}
            >
              <Group>
                <Label>{Drupal.t("Admin Label")}</Label>
                <Input
                  required
                  value={group.admin_label || ''}
                  name="admin_label"
                  type="text"
                  onChange={(e) => handleChange(group.id, e)}
                />
              </Group>
              <Group>
                <Label>{Drupal.t("Wrapper")}</Label>
                <Select
                  onChange={(e) => handleChange(group.id, e)}
                  required
                  value={group.wrapper || ''}
                  name="wrapper"
                >
                  <option value="div">div</option>
                  <option value="section">section</option>
                  <option value="article">article</option>
                </Select>
              </Group>
              <Group>
                <Label>{Drupal.t("Attributes")}</Label>
                <Input
                  onChange={(e) => handleChange(group.id, e)}
                  type="text"
                  name="attributes"
                  value={group.attributes || ''}
                />
              </Group>
              <Group>
                <Label>{Drupal.t("Blocks")}</Label>
                <Select
                  onChange={(e) => handleChange(group.id, e)}
                  required
                  name="blocks"
                  value={group.blocks}
                  multiple
                >
                  {$.map(blocks, (blockName, blockId) => (
                    <Option key={blockId} value={blockId}>
                      {blockName}
                    </Option>
                  ))}
                </Select>
              </Group>
            </SimpleAccordion>
          ))}
        </GroupsWrapper>
        <Button
          type="button button--primary js-form-submit form-submit"
          onClick={addGroup}
        >
          Add group
        </Button>
      </SimpleAccordion>
    </div>
  );
};

export default RegionInput;
+29 −0
Original line number Diff line number Diff line
import React, { useState } from "react";
import { AccordionTitle, DeleteGroup } from "./styled";

const SimpleAccordion = ({
  children,
  label,
  classNames,
  cancel = false,
  id,
  onCancel,
}) => {
  const [visible, setVisible] = useState(false);
  return (
    <div className={`blocks-wrapper-simple-accordion ${classNames}-accordion`}>
      <AccordionTitle onClick={() => setVisible(!visible)}>
        {label}
        {cancel && <DeleteGroup onClick={() => onCancel(id)}>X</DeleteGroup>}
      </AccordionTitle>
      <div
        className={`accordion-content`}
        style={{ display: visible ? "block" : "none" }}
      >
        {children}
      </div>
    </div>
  );
};

export default SimpleAccordion;
Loading