aboutsummaryrefslogtreecommitdiffstats
path: root/client/js/app/src/app/pages/querybuilder/Components/Text/QueryInputChild.jsx
blob: 0440d6ef1bae22981f606110a992567abadc6ccc (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
import React, { useContext } from 'react';
import AddPropertyButton from '../Buttons/AddPropertyButton';
import { QueryInputContext } from '../Contexts/QueryInputContext';
import QueryDropdownForm from './QueryDropDownForm';
import SimpleForm from './SimpleForm';
import { OverlayTrigger, Tooltip } from 'react-bootstrap';
import SimpleButton from '../Buttons/SimpleButton';

export default function QueryInputChild({ id }) {
  const { inputs, setInputs, childMap } = useContext(QueryInputContext);

  let index = inputs.findIndex((element) => element.id === id);
  let childArray = inputs[index].children;
  let currentTypes = inputs[index].type;

  /**
   * Update the state of inputs to reflect what is written into the form.
   * @param {Event} e Event containing the new input.
   */
  const updateInput = (e) => {
    e.preventDefault();
    let newInputs = inputs.slice();
    let iterId = e.target.id.replace('v', '');
    let currentId = iterId.substring(0, 1);
    let index = newInputs.findIndex((element) => element.id === currentId);
    let children = newInputs[index].children;
    const traversedChildren = traverseChildren(iterId, children, '');
    children = traversedChildren.children;
    index = children.findIndex((element) => element.id === iterId);
    children[index].input = e.target.value;
    setInputs(newInputs);
  };

  /**
   * Returns a placeholder text for a SimpleForm component.
   * @param {String} id The id of the SimpleForm component.
   * @returns {String} The placeholder text
   */
  const setPlaceHolder = (id) => {
    let currentId = id.substring(0, 1);
    let index = inputs.findIndex((element) => element.id === currentId);
    let combinedType = inputs[index].type;
    let children = inputs[index].children;
    if (id.length > 3) {
      const traversedChildren = traverseChildren(id, children, combinedType);
      combinedType = traversedChildren.combinedType;
      children = traversedChildren.children;
      const currentChoice = childMap[combinedType];
      index = children.findIndex((element) => element.id === id);
      combinedType = children[index].type;
      return currentChoice[combinedType].type;
    } else {
      const currentChoice = childMap[combinedType];
      index = children.findIndex((element) => element.id === id);
      combinedType = children[index].type;
      return currentChoice[combinedType].type;
    }
  };

  /**
   * Removes the row with the provided id.
   * @param {String} id Id of row.
   */
  const removeRow = (id) => {
    let newInputs = inputs.slice();
    let currentId = id.substring(0, 1);
    let index = newInputs.findIndex((element) => element.id === currentId);
    let children = newInputs[index].children;
    const traversedChildren = traverseChildren(id, children, '');
    index = traversedChildren.children.findIndex((element) => element === id);
    traversedChildren.children.splice(index, 1);
    setInputs(newInputs);
  };

  /**
   * Traverses the children until a child with the provided id is reached.
   * @param {String} id Id of the innermost child.
   * @param {Array} children Array containing serveral child objects.
   * @param {String} combinedType The combined type of all traversed children
   * @returns {Object} An object containing the children of the child with the provided id and the combined type.
   */
  function traverseChildren(id, children, combinedType) {
    let currentId;
    let index;
    for (let i = 3; i < id.length; i += 2) {
      currentId = id.substring(0, i);
      index = children.findIndex((element) => element.id === currentId);
      combinedType = combinedType + '_' + children[index].type;
      children = children[index].children;
    }
    return { children: children, combinedType: combinedType };
  }

  const inputList = childArray.map((child) => {
    return (
      <div key={child.id} id={child.id}>
        <QueryDropdownForm
          choices={childMap[currentTypes]}
          id={child.id}
          child={true}
        />
        {child.hasChildren ? (
          <>
            <AddPropertyButton id={child.id} />
          </>
        ) : (
          <SimpleForm
            id={`v${child.id}`}
            size="30"
            onChange={updateInput}
            placeholder={setPlaceHolder(child.id)}
          />
        )}
        <OverlayTrigger
          placement="right"
          delay={{ show: 250, hide: 400 }}
          overlay={<Tooltip id="button-tooltip">Remove row</Tooltip>}
        >
          <span>
            <SimpleButton
              id={`b${child.id}`}
              className="removeRow"
              onClick={() => removeRow(child.id)}
              children="-"
            ></SimpleButton>
          </span>
        </OverlayTrigger>
        <br />
        <Child
          type={currentTypes + '_' + child.type}
          child={child}
          onChange={updateInput}
          placeholder={setPlaceHolder}
          removeRow={removeRow}
        />
      </div>
    );
  });

  return <>{inputList}</>;
}

function Child({ child, type, onChange, placeholder, removeRow }) {
  const { childMap } = useContext(QueryInputContext);

  const nestedChildren = (child.children || []).map((child) => {
    return (
      <div key={child.id}>
        <QueryDropdownForm
          choices={childMap[type]}
          id={child.id}
          child={true}
        />
        {child.hasChildren ? (
          <>
            <AddPropertyButton id={child.id} />
          </>
        ) : (
          <SimpleForm
            id={`v${child.id}`}
            size="30"
            onChange={onChange}
            placeholder={placeholder(child.id)}
          />
        )}
        <OverlayTrigger
          placement="right"
          delay={{ show: 250, hide: 400 }}
          overlay={<Tooltip id="button-tooltip">Remove row</Tooltip>}
        >
          <span>
            <SimpleButton
              id={`b${child.id}`}
              className="removeRow"
              onClick={() => removeRow(child.id)}
              children="-"
            ></SimpleButton>
          </span>
        </OverlayTrigger>
        <br />
        <Child
          child={child}
          id={child.id}
          type={type + '_' + child.type}
          onChange={onChange}
          placeholder={placeholder}
          removeRow={removeRow}
        />
      </div>
    );
  });

  return <>{nestedChildren}</>;
}