@saltcorn/builder 1.6.0-alpha.6 → 1.6.0-alpha.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/builder_bundle.js +85 -1
  2. package/dist/builder_bundle.js.LICENSE.txt +65 -0
  3. package/package.json +32 -28
  4. package/src/components/Builder.js +334 -122
  5. package/src/components/Library.js +25 -13
  6. package/src/components/RenderNode.js +6 -6
  7. package/src/components/Toolbox.js +333 -269
  8. package/src/components/elements/Action.js +145 -30
  9. package/src/components/elements/Aggregation.js +20 -23
  10. package/src/components/elements/ArrayManager.js +7 -5
  11. package/src/components/elements/BoxModelEditor.js +19 -17
  12. package/src/components/elements/Card.js +47 -34
  13. package/src/components/elements/Clone.js +74 -2
  14. package/src/components/elements/Column.js +1 -1
  15. package/src/components/elements/Columns.js +27 -25
  16. package/src/components/elements/Container.js +170 -90
  17. package/src/components/elements/DropDownFilter.js +10 -8
  18. package/src/components/elements/DropMenu.js +8 -5
  19. package/src/components/elements/Field.js +9 -7
  20. package/src/components/elements/HTMLCode.js +3 -1
  21. package/src/components/elements/Image.js +20 -15
  22. package/src/components/elements/JoinField.js +15 -11
  23. package/src/components/elements/Link.js +18 -16
  24. package/src/components/elements/ListColumn.js +7 -3
  25. package/src/components/elements/ListColumns.js +4 -1
  26. package/src/components/elements/MonacoEditor.js +4 -2
  27. package/src/components/elements/Page.js +7 -4
  28. package/src/components/elements/RelationBadges.js +16 -11
  29. package/src/components/elements/RelationOnDemandPicker.js +18 -12
  30. package/src/components/elements/SearchBar.js +10 -6
  31. package/src/components/elements/Table.js +72 -65
  32. package/src/components/elements/Tabs.js +18 -15
  33. package/src/components/elements/Text.js +19 -14
  34. package/src/components/elements/ToggleFilter.js +28 -25
  35. package/src/components/elements/View.js +18 -11
  36. package/src/components/elements/ViewLink.js +15 -11
  37. package/src/components/elements/utils.js +224 -55
  38. package/src/components/storage.js +27 -129
  39. package/src/hooks/useTranslation.js +11 -0
  40. package/src/index.js +6 -3
@@ -16,9 +16,10 @@ import {
16
16
  buildOptions,
17
17
  } from "./utils";
18
18
  import { Column } from "./Column";
19
-
20
19
  import { Element, useNode } from "@craftjs/core";
21
20
  import { BoxModelEditor } from "./BoxModelEditor";
21
+ import useTranslation from "../../hooks/useTranslation";
22
+
22
23
  import {
23
24
  AlignTop,
24
25
  AlignMiddle,
@@ -40,7 +41,7 @@ import { bstyleopt } from "./utils";
40
41
 
41
42
  export /**
42
43
  * @param {object} props
43
- * @param {string} props.children
44
+ * @param {string} props.contents - Card body contents
44
45
  * @param {object} props.isFormula
45
46
  * @param {string} [props.title]
46
47
  * @param {string} props.shadow
@@ -52,7 +53,7 @@ export /**
52
53
  * @namespace
53
54
  */
54
55
  const Card = ({
55
- children,
56
+ contents,
56
57
  isFormula,
57
58
  title,
58
59
  shadow,
@@ -69,6 +70,7 @@ const Card = ({
69
70
  gradStartColor,
70
71
  gradEndColor,
71
72
  gradDirection,
73
+ titleRight,
72
74
  }) => {
73
75
  const {
74
76
  selected,
@@ -110,12 +112,17 @@ const Card = ({
110
112
  <img src={`/files/serve/${bgFileId}`} className="card-img-top" />
111
113
  ) : null}
112
114
  {title && title.length > 0 && (
113
- <div className="card-header">
115
+ <div className="card-header right-section">
114
116
  {isFormula?.title ? (
115
117
  <span className="font-monospace">={title}</span>
116
118
  ) : (
117
119
  title
118
120
  )}
121
+ <div className='title-right'>
122
+ <Element canvas id="titleRight" is={Column}>
123
+ {titleRight}
124
+ </Element>
125
+ </div>
119
126
  </div>
120
127
  )}
121
128
  <div
@@ -132,7 +139,9 @@ const Card = ({
132
139
  : {}
133
140
  }
134
141
  >
135
- <div className="canvas">{children}</div>
142
+ <Element canvas id="cardbody" is={Column}>
143
+ {contents}
144
+ </Element>
136
145
  </div>
137
146
  {hasFooter ? (
138
147
  <div className={`card-footer ${noPadding ? "p-0" : ""}`}>
@@ -152,6 +161,7 @@ export /**
152
161
  * @namespace
153
162
  */
154
163
  const CardSettings = () => {
164
+ const { t } = useTranslation();
155
165
  const node = useNode((node) => {
156
166
  const ps = {
157
167
  currentSettingsTab: node.data.props.currentSettingsTab,
@@ -186,11 +196,11 @@ const CardSettings = () => {
186
196
  value={currentSettingsTab}
187
197
  onChange={(ix) => setProp((prop) => (prop.currentSettingsTab = ix))}
188
198
  >
189
- <table className="w-100" accordiontitle="Card properties">
199
+ <table className="w-100" accordiontitle={t("Card properties")}>
190
200
  <tbody>
191
201
  <SettingsRow
192
202
  field={{
193
- label: "Card title",
203
+ label: t("Card title"),
194
204
  name: "title",
195
205
  type: "String",
196
206
  canBeFormula: true,
@@ -200,7 +210,7 @@ const CardSettings = () => {
200
210
  />
201
211
  <SettingsRow
202
212
  field={{
203
- label: "Click URL",
213
+ label: t("Click URL"),
204
214
  name: "url",
205
215
  type: "String",
206
216
  canBeFormula: true,
@@ -210,7 +220,7 @@ const CardSettings = () => {
210
220
  />
211
221
  <SettingsRow
212
222
  field={{
213
- label: "Class",
223
+ label: t("Class"),
214
224
  name: "class",
215
225
  type: "String",
216
226
  canBeFormula: true,
@@ -219,18 +229,18 @@ const CardSettings = () => {
219
229
  setProp={setProp}
220
230
  />
221
231
  <SettingsRow
222
- field={{ label: "Card footer", name: "hasFooter", type: "Bool" }}
232
+ field={{ label: t("Card footer"), name: "hasFooter", type: "Bool" }}
223
233
  node={node}
224
234
  setProp={setProp}
225
235
  />
226
236
  <SettingsRow
227
- field={{ label: "Shadow", name: "shadow", type: "Bool" }}
237
+ field={{ label: t("Shadow"), name: "shadow", type: "Bool" }}
228
238
  node={node}
229
239
  setProp={setProp}
230
240
  />
231
241
  <SettingsRow
232
242
  field={{
233
- label: "Save indicator",
243
+ label: t("Save indicator"),
234
244
  name: "titleAjaxIndicator",
235
245
  type: "Bool",
236
246
  }}
@@ -238,37 +248,37 @@ const CardSettings = () => {
238
248
  setProp={setProp}
239
249
  />
240
250
  <SettingsRow
241
- field={{ label: "No padding", name: "noPadding", type: "Bool" }}
251
+ field={{ label: t("No padding"), name: "noPadding", type: "Bool" }}
242
252
  node={node}
243
253
  setProp={setProp}
244
254
  />
245
255
  </tbody>
246
256
  </table>
247
- <div accordiontitle="Box" className="w-100">
257
+ <div accordiontitle={t("Box")} className="w-100">
248
258
  <BoxModelEditor setProp={setProp} node={node} sizeWithStyle={true} />
249
259
  </div>
250
- <table className="w-100" accordiontitle="Contents">
260
+ <table className="w-100" accordiontitle={t("Contents")}>
251
261
  <tbody>
252
- <SettingsSectionHeaderRow title="Align" />
262
+ <SettingsSectionHeaderRow title={t("Align")} />
253
263
  <SettingsRow
254
264
  field={{
255
265
  name: "hAlign",
256
- label: "Horizontal",
266
+ label: t("Horizontal"),
257
267
  type: "btn_select",
258
268
  options: [
259
- { value: "start", title: "Left", label: <AlignStart /> },
260
- { value: "center", title: "Center", label: <AlignCenter /> },
261
- { value: "end", title: "Right", label: <AlignEnd /> },
269
+ { value: "start", title: t("Left"), label: <AlignStart /> },
270
+ { value: "center", title: t("Center"), label: <AlignCenter /> },
271
+ { value: "end", title: t("Right"), label: <AlignEnd /> },
262
272
  ],
263
273
  }}
264
274
  node={node}
265
275
  setProp={setProp}
266
276
  />
267
- <SettingsSectionHeaderRow title="Background" />
277
+ <SettingsSectionHeaderRow title={t("Background")} />
268
278
  <SettingsRow
269
279
  field={{
270
280
  name: "bgType",
271
- label: "Type",
281
+ label: t("Type"),
272
282
  type: "btn_select",
273
283
  options: [
274
284
  { value: "None", label: <SlashCircle /> },
@@ -294,7 +304,7 @@ const CardSettings = () => {
294
304
  {bgType === "Gradient" && (
295
305
  <Fragment>
296
306
  <tr>
297
- <td>Start</td>
307
+ <td>{t("Start")}</td>
298
308
  <td>
299
309
  <OrFormula
300
310
  nodekey="gradStartColor"
@@ -310,7 +320,7 @@ const CardSettings = () => {
310
320
  </td>
311
321
  </tr>
312
322
  <tr>
313
- <td>End</td>
323
+ <td>{t("End")}</td>
314
324
  <td>
315
325
  <OrFormula
316
326
  nodekey="gradEndColor"
@@ -326,7 +336,7 @@ const CardSettings = () => {
326
336
  </td>
327
337
  </tr>
328
338
  <tr>
329
- <td>Direction (&deg;)</td>
339
+ <td>{t("Direction (&deg;)")}</td>
330
340
  <td>
331
341
  <OrFormula
332
342
  nodekey="gradDirection"
@@ -348,7 +358,7 @@ const CardSettings = () => {
348
358
  {bgType === "Image" && (
349
359
  <tr>
350
360
  <td>
351
- <label>File</label>
361
+ <label>{t("File")}</label>
352
362
  </td>
353
363
  <td>
354
364
  <select
@@ -373,7 +383,7 @@ const CardSettings = () => {
373
383
  {bgType === "Image Field" && (
374
384
  <tr>
375
385
  <td>
376
- <label>File field</label>
386
+ <label>{t("File field")}</label>
377
387
  </td>
378
388
  <td>
379
389
  <select
@@ -401,7 +411,7 @@ const CardSettings = () => {
401
411
  <Fragment>
402
412
  <tr>
403
413
  <td>
404
- <label>Location</label>
414
+ <label>{t("Location")}</label>
405
415
  </td>
406
416
 
407
417
  <td>
@@ -410,9 +420,9 @@ const CardSettings = () => {
410
420
  className="form-control-sm form-select"
411
421
  onChange={setAProp("imageLocation")}
412
422
  >
413
- <option>Card</option>
414
- <option>Body</option>
415
- <option>Top</option>
423
+ <option>{t("Card")}</option>
424
+ <option>{t("Body")}</option>
425
+ <option>{t("Top")}</option>
416
426
  </select>
417
427
  </td>
418
428
  </tr>
@@ -423,7 +433,7 @@ const CardSettings = () => {
423
433
  <Fragment>
424
434
  <tr>
425
435
  <td>
426
- <label>Size</label>
436
+ <label>{t("Size")}</label>
427
437
  </td>
428
438
 
429
439
  <td>
@@ -440,7 +450,7 @@ const CardSettings = () => {
440
450
  )}
441
451
  {bgType === "Color" && (
442
452
  <tr>
443
- <td>Color</td>
453
+ <td>{t("Color")}</td>
444
454
  <td>
445
455
  <OrFormula nodekey="bgColor" {...{ setProp, isFormula, node }}>
446
456
  <input
@@ -472,6 +482,8 @@ const fields = [
472
482
  { label: "Card footer", name: "hasFooter", type: "Bool" },
473
483
  { label: "Save indicator", name: "titleAjaxIndicator", type: "Bool" },
474
484
  { label: "No padding", name: "noPadding", type: "Bool" },
485
+ { label: "Contents", name: "contents", type: "Nodes", nodeID: "cardbody" },
486
+ { label: "Title Right", name: "titleRight", type: "Nodes", nodeID: "titleRight" },
475
487
  { label: "Footer", name: "footer", type: "Nodes", nodeID: "cardfooter" },
476
488
  { name: "style", default: {} },
477
489
  { label: "Class", name: "class", type: "String", canBeFormula: true },
@@ -498,13 +510,14 @@ Card.craft = {
498
510
  shadow: true,
499
511
  isFormula: {},
500
512
  style: {},
513
+ contents: [],
501
514
  footer: [],
515
+ titleRight: [],
502
516
  },
503
517
  displayName: "Card",
504
518
  related: {
505
519
  settings: CardSettings,
506
520
  segment_type: "card",
507
- hasContents: true,
508
521
  fields,
509
522
  },
510
523
  };
@@ -1,11 +1,26 @@
1
1
  import React, { Fragment, useState, useEffect } from "react";
2
2
  import { ListColumn } from "./ListColumn";
3
3
  import { Columns, ntimes } from "./Columns";
4
+ import { Card } from "./Card";
5
+ import { Container } from "./Container";
6
+ import { Tabs } from "./Tabs";
7
+ import { Table } from "./Table";
4
8
  import { Element } from "@craftjs/core";
5
9
 
6
10
  const rand_ident = () =>
7
11
  Math.floor(Math.random() * 16777215).toString(16);
8
12
 
13
+ /**
14
+ * Clone the children of a linked node (not the linked node wrapper itself).
15
+ * The parent component's render recreates the wrapper via <Element canvas>.
16
+ */
17
+ const cloneLinkedNodeChildren = (query, linkedNodeId) => {
18
+ if (!linkedNodeId) return undefined;
19
+ const linkedNodeData = query.node(linkedNodeId).get().data;
20
+ const childNodes = linkedNodeData.nodes || [];
21
+ return childNodes.map(recursivelyCloneToElems(query));
22
+ };
23
+
9
24
  export const recursivelyCloneToElems = (query) => (nodeId, ix) => {
10
25
  const { data } = query.node(nodeId).get();
11
26
  const { type, props, nodes } = data;
@@ -14,8 +29,6 @@ export const recursivelyCloneToElems = (query) => (nodeId, ix) => {
14
29
  newProps.rndid = rand_ident();
15
30
  }
16
31
 
17
- // console.log("cloning", data.displayName, data.linkedNodes["listcol"]);
18
-
19
32
  const children = (nodes || []).map(recursivelyCloneToElems(query));
20
33
  if (data.displayName === "Columns") {
21
34
  const cols = ntimes(data.props.ncols, (ix) =>
@@ -37,6 +50,65 @@ export const recursivelyCloneToElems = (query) => (nodeId, ix) => {
37
50
  });
38
51
  }
39
52
 
53
+ if (data.displayName === "Card") {
54
+ const clonedContents = cloneLinkedNodeChildren(
55
+ query,
56
+ data.linkedNodes["cardbody"]
57
+ );
58
+ const clonedFooter = cloneLinkedNodeChildren(
59
+ query,
60
+ data.linkedNodes["cardfooter"]
61
+ );
62
+ return React.createElement(Card, {
63
+ ...newProps,
64
+ ...(typeof ix !== "undefined" ? { key: ix } : {}),
65
+ contents: clonedContents,
66
+ footer: clonedFooter,
67
+ });
68
+ }
69
+
70
+ if (data.displayName === "Container") {
71
+ const clonedContents = cloneLinkedNodeChildren(
72
+ query,
73
+ data.linkedNodes["container-canvas"]
74
+ );
75
+ return React.createElement(Container, {
76
+ ...newProps,
77
+ ...(typeof ix !== "undefined" ? { key: ix } : {}),
78
+ contents: clonedContents,
79
+ });
80
+ }
81
+
82
+ if (data.displayName === "Tabs") {
83
+ const cols = ntimes(data.props.ntabs, (ix) =>
84
+ data.linkedNodes["Tab" + ix]
85
+ ? cloneLinkedNodeChildren(query, data.linkedNodes["Tab" + ix])
86
+ : undefined
87
+ );
88
+ return React.createElement(Tabs, {
89
+ ...newProps,
90
+ ...(typeof ix !== "undefined" ? { key: ix } : {}),
91
+ contents: cols,
92
+ });
93
+ }
94
+
95
+ if (data.displayName === "Table") {
96
+ const rows = data.props.rows || 0;
97
+ const columns = data.props.columns || 0;
98
+ const clonedContents = ntimes(rows, (ri) =>
99
+ ntimes(columns, (ci) =>
100
+ data.linkedNodes[`cell_${ri}_${ci}`]
101
+ ? cloneLinkedNodeChildren(query, data.linkedNodes[`cell_${ri}_${ci}`])
102
+ : undefined
103
+ )
104
+ );
105
+ return React.createElement(Table, {
106
+ ...newProps,
107
+ ...(typeof ix !== "undefined" ? { key: ix } : {}),
108
+ contents: clonedContents,
109
+ });
110
+ }
111
+
40
112
  if (data.isCanvas)
41
113
  return React.createElement(
42
114
  Element,
@@ -64,7 +64,7 @@ Column.craft = {
64
64
  props: {},
65
65
  rules: {
66
66
  canDrag: () => true,
67
- canMoveIn: (incomming, current) => {
67
+ canMoveIn: (incoming, current) => {
68
68
  if (current?.data?.props?.singleOccupancy && current.data.nodes?.length)
69
69
  return false;
70
70
 
@@ -6,6 +6,7 @@
6
6
 
7
7
  import React, { Fragment } from "react";
8
8
  import { Column } from "./Column";
9
+ import useTranslation from "../../hooks/useTranslation";
9
10
 
10
11
  import { Element, useNode } from "@craftjs/core";
11
12
  import {
@@ -124,6 +125,7 @@ export /**
124
125
  * @subcategory components
125
126
  */
126
127
  const ColumnsSettings = () => {
128
+ const { t } = useTranslation();
127
129
  const node = useNode((node) => ({
128
130
  widths: node.data.props.widths,
129
131
  ncols: node.data.props.ncols,
@@ -164,11 +166,11 @@ const ColumnsSettings = () => {
164
166
  value={currentSettingsTab}
165
167
  onChange={(ix) => setProp((prop) => (prop.currentSettingsTab = ix))}
166
168
  >
167
- <table accordiontitle="Column properties">
169
+ <table accordiontitle={t("Column properties")}>
168
170
  <tbody>
169
171
  <tr>
170
172
  <td colSpan="3">
171
- <label>Number of columns</label>
173
+ <label>{t("Number of columns")}</label>
172
174
  </td>
173
175
  <td>
174
176
  <input
@@ -190,7 +192,7 @@ const ColumnsSettings = () => {
190
192
  </td>
191
193
  </tr>
192
194
  <tr>
193
- <th colSpan="4">Widths &amp; Breakpoint</th>
195
+ <th colSpan="4">{t("Widths & Breakpoint")}</th>
194
196
  </tr>
195
197
  {ntimes(ncols, (ix) => (
196
198
  <Fragment key={ix}>
@@ -199,7 +201,7 @@ const ColumnsSettings = () => {
199
201
  </tr>
200
202
  <tr>
201
203
  <td>
202
- <label>Width</label>
204
+ <label>{t("Width")}</label>
203
205
  </td>
204
206
  <td align="right">
205
207
  {ix < ncols - 1 ? (
@@ -231,8 +233,8 @@ const ColumnsSettings = () => {
231
233
  setProp((prop) => (prop.breakpoints[ix] = value));
232
234
  }}
233
235
  >
234
- <option disabled>Breakpoint</option>
235
- <option value="">none</option>
236
+ <option disabled>{t("Breakpoint")}</option>
237
+ <option value="">{t("none")}</option>
236
238
  {buildBootstrapOptions(["sm", "md", "lg"])}
237
239
  </select>
238
240
  </td>
@@ -241,12 +243,12 @@ const ColumnsSettings = () => {
241
243
  ))}
242
244
  </tbody>
243
245
  </table>
244
- <div accordiontitle="Column settings">
245
- Settings for column #
246
+ <div accordiontitle={t("Column settings")}>
247
+ {t("Settings for column #")}
246
248
  <ConfigField
247
249
  field={{
248
250
  name: "setting_col_n",
249
- label: "Column number",
251
+ label: t("Column number"),
250
252
  type: "btn_select",
251
253
  options: ntimes(ncols, (i) => ({
252
254
  value: i + 1,
@@ -260,16 +262,16 @@ const ColumnsSettings = () => {
260
262
  ></ConfigField>
261
263
  <table className="w-100">
262
264
  <tbody>
263
- <SettingsSectionHeaderRow title="Align" />
265
+ <SettingsSectionHeaderRow title={t("Align")} />
264
266
  <SettingsRow
265
267
  field={{
266
268
  name: "vAlign",
267
- label: "Vertical",
269
+ label: t("Vertical"),
268
270
  type: "btn_select",
269
271
  options: [
270
- { value: "start", title: "Start", label: <AlignTop /> },
271
- { value: "center", title: "Center", label: <AlignMiddle /> },
272
- { value: "end", title: "End", label: <AlignBottom /> },
272
+ { value: "start", title: t("Start"), label: <AlignTop /> },
273
+ { value: "center", title: t("Center"), label: <AlignMiddle /> },
274
+ { value: "end", title: t("End"), label: <AlignBottom /> },
273
275
  ],
274
276
  }}
275
277
  node={colSetsNode}
@@ -284,12 +286,12 @@ const ColumnsSettings = () => {
284
286
  <SettingsRow
285
287
  field={{
286
288
  name: "hAlign",
287
- label: "Horizontal",
289
+ label: t("Horizontal"),
288
290
  type: "btn_select",
289
291
  options: [
290
- { value: "start", title: "Left", label: <AlignStart /> },
291
- { value: "center", title: "Center", label: <AlignCenter /> },
292
- { value: "end", title: "Right", label: <AlignEnd /> },
292
+ { value: "start", title: t("Left"), label: <AlignStart /> },
293
+ { value: "center", title: t("Center"), label: <AlignCenter /> },
294
+ { value: "end", title: t("Right"), label: <AlignEnd /> },
293
295
  ],
294
296
  }}
295
297
  node={colSetsNode}
@@ -304,7 +306,7 @@ const ColumnsSettings = () => {
304
306
  <SettingsRow
305
307
  field={{
306
308
  name: "colClass",
307
- label: "Class",
309
+ label: t("Class"),
308
310
  type: "String",
309
311
  }}
310
312
  node={colSetsNode}
@@ -319,7 +321,7 @@ const ColumnsSettings = () => {
319
321
  <SettingsRow
320
322
  field={{
321
323
  name: "colStyle",
322
- label: "CSS",
324
+ label: t("CSS"),
323
325
  type: "textarea",
324
326
  }}
325
327
  node={colSetsNode}
@@ -334,12 +336,12 @@ const ColumnsSettings = () => {
334
336
  </tbody>
335
337
  </table>
336
338
  </div>
337
- <table className="w-100" accordiontitle="Gutters and class">
339
+ <table className="w-100" accordiontitle={t("Gutters and class")}>
338
340
  <tbody>
339
341
  <SettingsRow
340
342
  field={{
341
343
  name: "gx",
342
- label: "Horizontal 0-5",
344
+ label: t("Horizontal 0-5"),
343
345
  type: "Integer",
344
346
  }}
345
347
  node={node}
@@ -348,7 +350,7 @@ const ColumnsSettings = () => {
348
350
  <SettingsRow
349
351
  field={{
350
352
  name: "gy",
351
- label: "Vertical 0-5",
353
+ label: t("Vertical 0-5"),
352
354
  type: "Integer",
353
355
  }}
354
356
  node={node}
@@ -357,7 +359,7 @@ const ColumnsSettings = () => {
357
359
  <SettingsRow
358
360
  field={{
359
361
  name: "customClass",
360
- label: "Custom class",
362
+ label: t("Custom class"),
361
363
  type: "String",
362
364
  }}
363
365
  node={node}
@@ -365,7 +367,7 @@ const ColumnsSettings = () => {
365
367
  />
366
368
  </tbody>
367
369
  </table>
368
- <div accordiontitle="Box" className="w-100">
370
+ <div accordiontitle={t("Box")} className="w-100">
369
371
  <BoxModelEditor setProp={setProp} node={node} sizeWithStyle={true} />
370
372
  </div>
371
373
  </Accordion>