@saltcorn/builder 0.9.4-beta.2 → 0.9.4-beta.21

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.
@@ -9,6 +9,7 @@ import { useNode } from "@craftjs/core";
9
9
  import optionsCtx from "../context";
10
10
  import previewCtx from "../preview_context";
11
11
  import relationsCtx from "../relations_context";
12
+ import Select from "react-select";
12
13
 
13
14
  import {
14
15
  fetchViewPreview,
@@ -17,13 +18,18 @@ import {
17
18
  buildOptions,
18
19
  HelpTopicLink,
19
20
  initialRelation,
20
- prepCacheAndFinder,
21
- updateRelationsCache,
21
+ buildLayers,
22
22
  } from "./utils";
23
23
 
24
24
  import { RelationBadges } from "./RelationBadges";
25
25
  import { RelationOnDemandPicker } from "./RelationOnDemandPicker";
26
26
 
27
+ import {
28
+ RelationsFinder,
29
+ Relation,
30
+ buildTableCaches,
31
+ } from "@saltcorn/common-code";
32
+
27
33
  export /**
28
34
  * @param {object} props
29
35
  * @param {*} props.name
@@ -48,7 +54,6 @@ const View = ({ name, view, configuration, state }) => {
48
54
  if (rest.startsWith(".")) viewname = prefix;
49
55
  else viewname = rest;
50
56
  }
51
-
52
57
  const theview = options.views.find((v) => v.name === viewname);
53
58
  const label = theview ? theview.label : view;
54
59
  const { previews, setPreviews } = React.useContext(previewCtx);
@@ -109,10 +114,23 @@ const ViewSettings = () => {
109
114
  extra_state_fml,
110
115
  } = node;
111
116
  const options = React.useContext(optionsCtx);
112
- const { caches, finder } = useMemo(
113
- () => prepCacheAndFinder(options),
114
- [undefined]
115
- );
117
+ const {
118
+ tables,
119
+ views,
120
+ max_relations_layer_depth,
121
+ tableName,
122
+ excluded_subview_templates,
123
+ } = options;
124
+ // not needed in page editor
125
+ let finder = null;
126
+ let tableCaches = null;
127
+ if (tables && views) {
128
+ finder = useMemo(
129
+ () => new RelationsFinder(tables, views, max_relations_layer_depth),
130
+ [undefined]
131
+ );
132
+ tableCaches = useMemo(() => buildTableCaches(tables), [undefined]);
133
+ }
116
134
  const fixed_state_fields =
117
135
  options.fixed_state_fields && options.fixed_state_fields[view];
118
136
  const { setPreviews } = React.useContext(previewCtx);
@@ -134,87 +152,118 @@ const ViewSettings = () => {
134
152
  if (rest.startsWith(".")) viewname = prefix;
135
153
  else viewname = rest;
136
154
  }
137
- if (viewname.includes(".")) viewname = viewname.split(".")[0];
138
- if (finder)
139
- updateRelationsCache(
140
- relationsCache,
141
- setRelationsCache,
142
- options,
143
- finder,
144
- viewname
155
+ if (viewname && viewname.includes(".")) viewname = viewname.split(".")[0];
156
+
157
+ if (
158
+ finder &&
159
+ !(relationsCache[tableName] && relationsCache[tableName][viewname])
160
+ ) {
161
+ const relations = finder.findRelations(
162
+ tableName,
163
+ viewname,
164
+ excluded_subview_templates
145
165
  );
146
- const [relations, setRelations] = finder
147
- ? React.useState(relationsCache[options.tableName][viewname])
166
+ const layers = buildLayers(
167
+ relations,
168
+ tableName,
169
+ tableCaches.tableNameCache
170
+ );
171
+ relationsCache[tableName] = relationsCache[tableName] || {};
172
+ relationsCache[tableName][viewname] = { relations, layers };
173
+ setRelationsCache({ ...relationsCache });
174
+ }
175
+ const [relationsData, setRelationsData] = finder
176
+ ? React.useState(relationsCache[tableName][viewname])
148
177
  : [undefined, undefined];
149
- let safeRelation = relation;
178
+ let safeRelation = null;
179
+ const subView = views.find((view) => view.name === viewname);
180
+ if (relation && subView) {
181
+ const subTbl = tables.find((tbl) => tbl.id === subView.table_id);
182
+ safeRelation = new Relation(
183
+ relation,
184
+ subTbl ? subTbl.name : "",
185
+ subView.display_type
186
+ );
187
+ }
150
188
  if (
151
189
  options.mode !== "filter" &&
190
+ subView?.table_id &&
152
191
  !safeRelation &&
153
192
  !hasLegacyRelation &&
154
- relations?.paths.length > 0
193
+ relationsData?.relations.length > 0
155
194
  ) {
156
- safeRelation = initialRelation(relations.paths, options.tableName);
195
+ safeRelation = initialRelation(relationsData.relations);
157
196
  setProp((prop) => {
158
- prop.relation = safeRelation;
197
+ prop.relation = safeRelation.relationString;
159
198
  });
160
199
  }
161
200
  const helpContext = { view_name: viewname };
162
201
  if (options.tableName) helpContext.srcTable = options.tableName;
163
202
  const set_view_name = (e) => {
164
- if (e.target) {
165
- const target_value = e.target.value;
203
+ if (e?.target?.value || e?.value) {
204
+ const target_value = e.target?.value || e.value;
166
205
  if (target_value !== viewname) {
167
206
  if (options.mode === "filter") {
168
207
  setProp((prop) => {
169
208
  prop.view = target_value;
170
209
  });
171
210
  } else {
172
- updateRelationsCache(
173
- relationsCache,
174
- setRelationsCache,
175
- options,
176
- finder,
177
- target_value
211
+ const newRelations = finder.findRelations(
212
+ tableName,
213
+ target_value,
214
+ excluded_subview_templates
215
+ );
216
+ const layers = buildLayers(
217
+ newRelations,
218
+ tableName,
219
+ tableCaches.tableNameCache
178
220
  );
179
- const newRelations = relationsCache[options.tableName][target_value];
180
- if (newRelations.paths.length > 0) {
221
+ relationsCache[tableName] = relationsCache[tableName] || {};
222
+ relationsCache[tableName][target_value] = {
223
+ relations: newRelations,
224
+ layers,
225
+ };
226
+ if (newRelations.length > 0) {
181
227
  setProp((prop) => {
182
228
  prop.view = target_value;
183
- prop.relation = initialRelation(
184
- newRelations.paths,
185
- options.tableName
186
- );
229
+ prop.relation = initialRelation(newRelations).relationString;
230
+ });
231
+ setRelationsData({ relations: newRelations, layers });
232
+ } else
233
+ window.notifyAlert({
234
+ type: "warning",
235
+ text: `${target_value} has no relations`,
187
236
  });
188
- setRelations(newRelations);
189
- }
190
237
  }
191
238
  }
192
239
  }
193
240
  };
194
-
241
+ const viewOptions = options.views.map(({ name, label }) => ({
242
+ label,
243
+ value: name,
244
+ }));
245
+ const selectedView = viewOptions.find((v) => v.value === viewname);
195
246
  return (
196
247
  <div>
197
- {relations ? (
248
+ {relationsData ? (
198
249
  <Fragment>
199
250
  <div>
200
251
  <label>View to {options.mode === "show" ? "embed" : "show"}</label>
201
- <select
202
- value={viewname}
203
- className="form-control form-select"
204
- onChange={set_view_name}
205
- onBlur={set_view_name}
206
- >
207
- {options.views.map((v, ix) => (
208
- <option key={ix} value={v.name}>
209
- {v.label}
210
- </option>
211
- ))}
212
- </select>
252
+ {options.inJestTestingMode ? null : (
253
+ <Select
254
+ options={viewOptions}
255
+ value={selectedView}
256
+ onChange={set_view_name}
257
+ onBlur={set_view_name}
258
+ menuPortalTarget={document.body}
259
+ styles={{ menuPortal: (base) => ({ ...base, zIndex: 19999 }) }}
260
+ ></Select>
261
+ )}
213
262
  </div>
214
263
  {options.mode !== "filter" && (
215
264
  <div>
216
265
  <RelationOnDemandPicker
217
- relations={relations.layers}
266
+ relations={relationsData.layers}
218
267
  update={(relPath) => {
219
268
  if (relPath.startsWith(".")) {
220
269
  setProp((prop) => {
@@ -232,8 +281,8 @@ const ViewSettings = () => {
232
281
  <RelationBadges
233
282
  view={view}
234
283
  relation={safeRelation}
235
- parentTbl={options.tableName}
236
- tableNameCache={caches.tableNameCache}
284
+ parentTbl={tableName}
285
+ caches={tableCaches}
237
286
  />
238
287
  </div>
239
288
  )}
@@ -241,18 +290,26 @@ const ViewSettings = () => {
241
290
  ) : (
242
291
  <div>
243
292
  <label>View to {options.mode === "show" ? "embed" : "show"}</label>
244
- <select
245
- value={view}
246
- className="form-control form-select"
247
- onChange={setAProp("view")}
248
- onBlur={setAProp("view")}
249
- >
250
- {options.views.map((f, ix) => (
251
- <option key={ix} value={f.name}>
252
- {f.label || f.name}
253
- </option>
254
- ))}
255
- </select>
293
+ {options.inJestTestingMode ? null : (
294
+ <Select
295
+ options={viewOptions}
296
+ value={selectedView}
297
+ onChange={(e) => {
298
+ const target_value = e?.target?.value || e?.value;
299
+ setProp((prop) => {
300
+ prop.view = target_value;
301
+ });
302
+ }}
303
+ onBlur={(e) => {
304
+ const target_value = e?.target?.value || e?.value;
305
+ setProp((prop) => {
306
+ prop.view = target_value;
307
+ });
308
+ }}
309
+ menuPortalTarget={document.body}
310
+ styles={{ menuPortal: (base) => ({ ...base, zIndex: 19999 }) }}
311
+ ></Select>
312
+ )}
256
313
  </div>
257
314
  )}
258
315
  {options.mode !== "edit" && (
@@ -293,7 +350,7 @@ const ViewSettings = () => {
293
350
  )}
294
351
  </Fragment>
295
352
  )}
296
- {(state === "shared" || options.mode === "page") && (
353
+ {
297
354
  <Fragment>
298
355
  <label>
299
356
  Extra state Formula
@@ -311,7 +368,7 @@ const ViewSettings = () => {
311
368
  </small>
312
369
  ) : null}
313
370
  </Fragment>
314
- )}
371
+ }
315
372
  {view ? (
316
373
  <a
317
374
  className="d-block mt-2"
@@ -18,13 +18,19 @@ import {
18
18
  setAPropGen,
19
19
  FormulaTooltip,
20
20
  HelpTopicLink,
21
- prepCacheAndFinder,
22
21
  initialRelation,
23
- updateRelationsCache,
22
+ buildLayers,
24
23
  } from "./utils";
25
24
 
26
25
  import { RelationBadges } from "./RelationBadges";
27
26
  import { RelationOnDemandPicker } from "./RelationOnDemandPicker";
27
+ import Select from "react-select";
28
+
29
+ import {
30
+ RelationsFinder,
31
+ Relation,
32
+ buildTableCaches,
33
+ } from "@saltcorn/common-code";
28
34
 
29
35
  export /**
30
36
  * @param {object} props
@@ -127,10 +133,18 @@ const ViewLinkSettings = () => {
127
133
  link_target_blank,
128
134
  } = node;
129
135
  const options = React.useContext(optionsCtx);
130
- const { caches, finder } = useMemo(
131
- () => prepCacheAndFinder(options),
136
+ const {
137
+ tables,
138
+ views,
139
+ tableName,
140
+ excluded_subview_templates,
141
+ max_relations_layer_depth,
142
+ } = options;
143
+ const finder = useMemo(
144
+ () => new RelationsFinder(tables, views, max_relations_layer_depth),
132
145
  [undefined]
133
146
  );
147
+ const tableCaches = useMemo(() => buildTableCaches(tables), [undefined]);
134
148
  const { relationsCache, setRelationsCache } = React.useContext(relationsCtx);
135
149
  let errorString = false;
136
150
  try {
@@ -147,50 +161,87 @@ const ViewLinkSettings = () => {
147
161
  const safeViewName = use_view_name?.includes(".")
148
162
  ? use_view_name.split(".")[0]
149
163
  : use_view_name;
150
- updateRelationsCache(
151
- relationsCache,
152
- setRelationsCache,
153
- options,
154
- finder,
155
- safeViewName
156
- );
157
- const [relations, setRelations] = React.useState(
164
+ const subView = views.find((view) => view.name === safeViewName);
165
+ const hasTableId = subView?.table_id !== undefined;
166
+ if (!(relationsCache[tableName] && relationsCache[tableName][safeViewName])) {
167
+ const relations = finder.findRelations(
168
+ tableName,
169
+ safeViewName,
170
+ excluded_subview_templates
171
+ );
172
+ const layers = buildLayers(
173
+ relations,
174
+ tableName,
175
+ tableCaches.tableNameCache
176
+ );
177
+ relationsCache[tableName] = relationsCache[tableName] || {};
178
+ relationsCache[tableName][safeViewName] = { relations, layers };
179
+ setRelationsCache({ ...relationsCache });
180
+ }
181
+ const [relationsData, setRelationsData] = React.useState(
158
182
  relationsCache[options.tableName][safeViewName]
159
183
  );
160
- let safeRelation = relation;
161
- if (!safeRelation && !hasLegacyRelation && relations?.paths.length > 0) {
162
- safeRelation = initialRelation(relations.paths, options.tableName);
184
+ let safeRelation = null;
185
+ if (relation) {
186
+ const subView = views.find((view) => view.name === safeViewName);
187
+ const subTbl = tables.find((tbl) => tbl.id === subView.table_id);
188
+ safeRelation = new Relation(
189
+ relation,
190
+ subTbl ? subTbl.name : "",
191
+ subView.display_type
192
+ );
193
+ }
194
+ if (
195
+ !safeRelation &&
196
+ !hasLegacyRelation &&
197
+ relationsData?.relations.length > 0
198
+ ) {
199
+ safeRelation = initialRelation(relationsData.relations);
163
200
  setProp((prop) => {
164
- prop.relation = safeRelation;
201
+ prop.relation = safeRelation.relationString;
165
202
  });
166
203
  }
167
204
  const set_view_name = (e) => {
168
- if (e.target) {
169
- const target_value = e.target.value;
205
+ if (e?.target?.value || e?.value) {
206
+ const target_value = e.target?.value || e.value;
170
207
  if (target_value !== use_view_name) {
171
- updateRelationsCache(
172
- relationsCache,
173
- setRelationsCache,
174
- options,
175
- finder,
176
- target_value
208
+ const newRelations = finder.findRelations(
209
+ tableName,
210
+ target_value,
211
+ excluded_subview_templates
177
212
  );
178
- const newRelations = relationsCache[options.tableName][target_value];
179
- if (newRelations.paths.length > 0) {
213
+ const layers = buildLayers(
214
+ newRelations,
215
+ tableName,
216
+ tableCaches.tableNameCache
217
+ );
218
+
219
+ relationsCache[tableName] = relationsCache[tableName] || {};
220
+ relationsCache[tableName][target_value] = {
221
+ relations: newRelations,
222
+ layers,
223
+ };
224
+ if (newRelations.length > 0) {
180
225
  setProp((prop) => {
181
226
  prop.name = target_value;
182
- prop.relation = initialRelation(
183
- newRelations.paths,
184
- options.tableName
185
- );
227
+ prop.relation = initialRelation(newRelations).relationString;
228
+ });
229
+ setRelationsData({ relations: newRelations, layers });
230
+ } else
231
+ window.notifyAlert({
232
+ type: "warning",
233
+ text: `${target_value} has no relations`,
186
234
  });
187
- setRelations(newRelations);
188
- }
189
235
  }
190
236
  }
191
237
  };
192
238
  const helpContext = { view_name: use_view_name };
193
- if (options.tableName) helpContext.srcTable = options.tableName;
239
+ if (tableName) helpContext.srcTable = tableName;
240
+ const viewOptions = options.views.map(({ name, label }) => ({
241
+ label,
242
+ value: name,
243
+ }));
244
+ const selectedView = viewOptions.find((v) => v.value === use_view_name);
194
245
  return (
195
246
  <div>
196
247
  <table className="w-100">
@@ -198,24 +249,24 @@ const ViewLinkSettings = () => {
198
249
  <tr>
199
250
  <td colSpan="2">
200
251
  <label>View to link to</label>
201
- <select
202
- value={use_view_name}
203
- className="form-control form-select"
204
- onChange={set_view_name}
205
- onBlur={set_view_name}
206
- >
207
- {options.views.map((f, ix) => (
208
- <option key={ix} value={f.name}>
209
- {f.label}
210
- </option>
211
- ))}
212
- </select>
252
+ {options.inJestTestingMode ? null : (
253
+ <Select
254
+ options={viewOptions}
255
+ value={selectedView}
256
+ onChange={set_view_name}
257
+ onBlur={set_view_name}
258
+ menuPortalTarget={document.body}
259
+ styles={{
260
+ menuPortal: (base) => ({ ...base, zIndex: 19999 }),
261
+ }}
262
+ ></Select>
263
+ )}
213
264
  </td>
214
265
  </tr>
215
266
  <tr>
216
267
  <td colSpan="2">
217
268
  <RelationOnDemandPicker
218
- relations={relations.layers}
269
+ relations={relationsData.layers}
219
270
  update={(relPath) => {
220
271
  if (relPath.startsWith(".")) {
221
272
  setProp((prop) => {
@@ -233,8 +284,8 @@ const ViewLinkSettings = () => {
233
284
  <RelationBadges
234
285
  view={name}
235
286
  relation={safeRelation}
236
- parentTbl={options.tableName}
237
- tableNameCache={caches.tableNameCache}
287
+ parentTbl={tableName}
288
+ caches={tableCaches}
238
289
  />
239
290
  </td>
240
291
  </tr>
@@ -278,6 +329,7 @@ const ViewLinkSettings = () => {
278
329
  values={node}
279
330
  linkFirst={true}
280
331
  linkIsBlank={true}
332
+ allowRunOnLoad={false}
281
333
  />
282
334
  </tbody>
283
335
  </table>