@jskit-ai/crud-ui-generator 0.1.49 → 0.1.50
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.
- package/package.descriptor.mjs +29 -4
- package/package.json +4 -4
- package/src/server/buildTemplateContext.js +169 -9
- package/src/server/resourceSupport.js +78 -62
- package/templates/src/pages/admin/ui-generator/AddEditForm.vue +17 -86
- package/templates/src/pages/admin/ui-generator/EditElement.vue +18 -53
- package/templates/src/pages/admin/ui-generator/EditWrapperElement.vue +9 -20
- package/templates/src/pages/admin/ui-generator/ListElement.vue +38 -116
- package/templates/src/pages/admin/ui-generator/NewElement.vue +17 -44
- package/templates/src/pages/admin/ui-generator/NewWrapperElement.vue +8 -8
- package/templates/src/pages/admin/ui-generator/ViewElement.vue +18 -72
- package/templates/src/pages/admin/ui-generator/listBulkActions.js +5 -0
- package/templates/src/pages/admin/ui-generator/listFilters.js +5 -0
- package/test/addFieldSubcommand.test.js +8 -8
- package/test/buildTemplateContext.test.js +182 -4
- package/test/packageDescriptor.test.js +47 -2
package/package.descriptor.mjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { GENERATED_UI_NAVIGATION_ROLE_OPTION } from "@jskit-ai/kernel/shared/support/generatedUiContract";
|
|
2
|
+
|
|
1
3
|
export default Object.freeze({
|
|
2
4
|
packageVersion: 1,
|
|
3
5
|
packageId: "@jskit-ai/crud-ui-generator",
|
|
4
|
-
version: "0.1.
|
|
6
|
+
version: "0.1.50",
|
|
5
7
|
kind: "generator",
|
|
6
8
|
description: "Generate CRUD route trees from resource validators at an explicit route root relative to src/pages/.",
|
|
7
9
|
options: {
|
|
@@ -66,6 +68,7 @@ export default Object.freeze({
|
|
|
66
68
|
promptLabel: "Link placement",
|
|
67
69
|
promptHint: "Optional semantic target override for the generated list-page link placement (format: area.slot)."
|
|
68
70
|
},
|
|
71
|
+
"navigation-role": GENERATED_UI_NAVIGATION_ROLE_OPTION,
|
|
69
72
|
namespace: {
|
|
70
73
|
required: false,
|
|
71
74
|
inputType: "text",
|
|
@@ -110,6 +113,7 @@ export default Object.freeze({
|
|
|
110
113
|
"display-fields",
|
|
111
114
|
"id-param",
|
|
112
115
|
"parent-title",
|
|
116
|
+
"navigation-role",
|
|
113
117
|
"link-placement",
|
|
114
118
|
"namespace",
|
|
115
119
|
"force"
|
|
@@ -171,7 +175,7 @@ export default Object.freeze({
|
|
|
171
175
|
mutations: {
|
|
172
176
|
dependencies: {
|
|
173
177
|
runtime: {
|
|
174
|
-
"@jskit-ai/users-web": "0.1.
|
|
178
|
+
"@jskit-ai/users-web": "0.1.82"
|
|
175
179
|
},
|
|
176
180
|
dev: {}
|
|
177
181
|
},
|
|
@@ -195,6 +199,28 @@ export default Object.freeze({
|
|
|
195
199
|
in: ["list"]
|
|
196
200
|
}
|
|
197
201
|
},
|
|
202
|
+
{
|
|
203
|
+
from: "templates/src/pages/admin/ui-generator/listFilters.js",
|
|
204
|
+
to: "src/pages/${option:target-root|trim}/listFilters.js",
|
|
205
|
+
reason: "Install generated client-side list filter definitions.",
|
|
206
|
+
category: "crud-ui-generator",
|
|
207
|
+
id: "crud-ui-page-list-filters-${option:target-root|snake}",
|
|
208
|
+
when: {
|
|
209
|
+
option: "operations",
|
|
210
|
+
in: ["list"]
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
from: "templates/src/pages/admin/ui-generator/listBulkActions.js",
|
|
215
|
+
to: "src/pages/${option:target-root|trim}/listBulkActions.js",
|
|
216
|
+
reason: "Install generated client-side list bulk action definitions.",
|
|
217
|
+
category: "crud-ui-generator",
|
|
218
|
+
id: "crud-ui-page-list-bulk-actions-${option:target-root|snake}",
|
|
219
|
+
when: {
|
|
220
|
+
option: "operations",
|
|
221
|
+
in: ["list"]
|
|
222
|
+
}
|
|
223
|
+
},
|
|
198
224
|
{
|
|
199
225
|
from: "templates/src/pages/admin/ui-generator/ViewElement.vue",
|
|
200
226
|
to: "src/pages/${option:target-root|trim}/[${option:id-param|trim}]/index.vue",
|
|
@@ -355,8 +381,7 @@ export default Object.freeze({
|
|
|
355
381
|
file: "src/placement.js",
|
|
356
382
|
position: "bottom",
|
|
357
383
|
skipIfContains: "__JSKIT_UI_MENU_MARKER__",
|
|
358
|
-
value:
|
|
359
|
-
"\n// __JSKIT_UI_MENU_MARKER__\n{\n addPlacement({\n id: \"__JSKIT_UI_MENU_PLACEMENT_ID__\",\n target: \"__JSKIT_UI_MENU_PLACEMENT_TARGET__\",\n__JSKIT_UI_MENU_OWNER_LINE__ kind: \"link\",\n surfaces: [\"__JSKIT_UI_SURFACE_ID__\"],\n order: 155,\n props: {\n label: \"__JSKIT_UI_MENU_LABEL__\",\n icon: \"__JSKIT_UI_MENU_ICON__\",\n surface: \"__JSKIT_UI_SURFACE_ID__\",\n scopedSuffix: \"__JSKIT_UI_MENU_WORKSPACE_SUFFIX__\",\n unscopedSuffix: \"__JSKIT_UI_MENU_NON_WORKSPACE_SUFFIX__\",\n__JSKIT_UI_MENU_TO_PROP_LINE__ },\n__JSKIT_UI_MENU_WHEN_LINE__ });\n}\n",
|
|
384
|
+
value: "__JSKIT_UI_MENU_APPEND_BLOCK__",
|
|
360
385
|
reason: "Append generated CRUD list-page placement.",
|
|
361
386
|
category: "crud-ui-generator",
|
|
362
387
|
id: "crud-ui-placement-menu",
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jskit-ai/crud-ui-generator",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.50",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "node --test"
|
|
7
7
|
},
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"@jskit-ai/crud-core": "0.1.
|
|
10
|
-
"@jskit-ai/kernel": "0.1.
|
|
11
|
-
"@jskit-ai/resource-crud-core": "0.1.
|
|
9
|
+
"@jskit-ai/crud-core": "0.1.75",
|
|
10
|
+
"@jskit-ai/kernel": "0.1.67",
|
|
11
|
+
"@jskit-ai/resource-crud-core": "0.1.12"
|
|
12
12
|
},
|
|
13
13
|
"exports": {
|
|
14
14
|
"./server/buildTemplateContext": "./src/server/buildTemplateContext.js"
|
|
@@ -7,6 +7,10 @@ import {
|
|
|
7
7
|
resolvePageTargetDetails
|
|
8
8
|
} from "@jskit-ai/kernel/server/support";
|
|
9
9
|
import { normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
|
|
10
|
+
import {
|
|
11
|
+
resolveGeneratedUiNavigationRoleLinkPlacement,
|
|
12
|
+
shouldCreateGeneratedUiNavigationLink
|
|
13
|
+
} from "@jskit-ai/kernel/shared/support/generatedUiContract";
|
|
10
14
|
import {
|
|
11
15
|
loadResourceDefinition,
|
|
12
16
|
requireOperation,
|
|
@@ -22,6 +26,7 @@ import {
|
|
|
22
26
|
resolveNearestParentRouteParamKey,
|
|
23
27
|
buildListHeaderColumns,
|
|
24
28
|
buildListRowColumns,
|
|
29
|
+
buildListCardFields,
|
|
25
30
|
buildViewColumns,
|
|
26
31
|
buildFormColumns,
|
|
27
32
|
resolveRecordIdFieldKey,
|
|
@@ -157,6 +162,18 @@ function toTitleFromKebab(value = "", fallback = "") {
|
|
|
157
162
|
return wordsToTitle(words);
|
|
158
163
|
}
|
|
159
164
|
|
|
165
|
+
function buildCrudListCopy(resourceLabels = {}) {
|
|
166
|
+
const pluralTitle = normalizeText(resourceLabels.pluralTitle) || "Records";
|
|
167
|
+
const singularTitle = normalizeText(resourceLabels.singularTitle) || "Record";
|
|
168
|
+
return Object.freeze({
|
|
169
|
+
loadErrorTitle: `Unable to load ${pluralTitle}`,
|
|
170
|
+
loadErrorBody: "Check the connection and try again.",
|
|
171
|
+
emptyTitle: `No ${pluralTitle} yet`,
|
|
172
|
+
emptyBody: `Create the first ${singularTitle} to start using this workflow.`,
|
|
173
|
+
createLabel: `New ${singularTitle}`
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
160
177
|
function normalizeRelativeAppPath(value = "") {
|
|
161
178
|
return String(value || "")
|
|
162
179
|
.replaceAll("\\", "/")
|
|
@@ -242,6 +259,20 @@ function parseParentTitleOption(options) {
|
|
|
242
259
|
return parentTitleMode;
|
|
243
260
|
}
|
|
244
261
|
|
|
262
|
+
function shouldCreateNavigationLink(options = {}, inferenceContext = {}) {
|
|
263
|
+
return shouldCreateGeneratedUiNavigationLink(options, {
|
|
264
|
+
dynamicRoutePolicy: "any",
|
|
265
|
+
routePath: inferenceContext?.routePath
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function resolveNavigationRoleLinkPlacement(options = {}, inferenceContext = {}) {
|
|
270
|
+
return resolveGeneratedUiNavigationRoleLinkPlacement(options, {
|
|
271
|
+
dynamicRoutePolicy: "any",
|
|
272
|
+
routePath: inferenceContext?.routePath
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
|
|
245
276
|
function validateDisplayFieldsForOperation(selectedFieldKeys, fields, operationName) {
|
|
246
277
|
const selectedFields = Array.isArray(selectedFieldKeys) ? selectedFieldKeys : [];
|
|
247
278
|
if (selectedFields.length < 1) {
|
|
@@ -303,6 +334,58 @@ function hasLookupFormFields(fields = []) {
|
|
|
303
334
|
return (Array.isArray(fields) ? fields : []).some((field) => normalizeText(field?.component).toLowerCase() === "lookup");
|
|
304
335
|
}
|
|
305
336
|
|
|
337
|
+
function hasLookupDisplayFields(fields = []) {
|
|
338
|
+
return (Array.isArray(fields) ? fields : []).some((field) => normalizeText(field?.relation?.kind).toLowerCase() === "lookup");
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function buildListCardSlotProps(fields = []) {
|
|
342
|
+
const normalizedFields = Array.isArray(fields) ? fields : [];
|
|
343
|
+
if (normalizedFields.length < 1) {
|
|
344
|
+
return "{}";
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const props = ["record"];
|
|
348
|
+
if (hasLookupDisplayFields(normalizedFields)) {
|
|
349
|
+
props.push("records");
|
|
350
|
+
}
|
|
351
|
+
props.push("formatListCardValue");
|
|
352
|
+
return `{ ${props.join(", ")} }`;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function buildListRowSlotProps(fields = []) {
|
|
356
|
+
const normalizedFields = Array.isArray(fields) ? fields : [];
|
|
357
|
+
if (normalizedFields.length < 1) {
|
|
358
|
+
return "{}";
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const props = ["record"];
|
|
362
|
+
if (hasLookupDisplayFields(normalizedFields)) {
|
|
363
|
+
props.push("records");
|
|
364
|
+
}
|
|
365
|
+
return `{ ${props.join(", ")} }`;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function buildCrudFieldsSlotProps(fields = [], { includeMode = false } = {}) {
|
|
369
|
+
const props = [];
|
|
370
|
+
if (includeMode) {
|
|
371
|
+
props.push("mode");
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
props.push("formState", "addEdit", "resolveFieldErrors");
|
|
375
|
+
if (hasLookupFormFields(fields)) {
|
|
376
|
+
props.push(
|
|
377
|
+
"resolveLookupItems: fieldLookupItems",
|
|
378
|
+
"resolveLookupLoading: fieldLookupLoading",
|
|
379
|
+
"resolveLookupSearch: fieldLookupSearch",
|
|
380
|
+
"setLookupSearch: setFieldLookupSearch"
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return `{
|
|
385
|
+
${props.join(",\n ")}
|
|
386
|
+
}`;
|
|
387
|
+
}
|
|
388
|
+
|
|
306
389
|
function buildLookupImportLine(fields = []) {
|
|
307
390
|
return hasLookupFormFields(fields)
|
|
308
391
|
? 'import { createCrudLookupFieldRuntime } from "@jskit-ai/users-web/client/composables/crudLookupFieldRuntime";'
|
|
@@ -339,16 +422,17 @@ const {
|
|
|
339
422
|
`;
|
|
340
423
|
}
|
|
341
424
|
|
|
342
|
-
function buildLookupFormProps(fields = []) {
|
|
425
|
+
function buildLookupFormProps(fields = [], { sourcePrefix = "" } = {}) {
|
|
343
426
|
if (!hasLookupFormFields(fields)) {
|
|
344
427
|
return "";
|
|
345
428
|
}
|
|
346
429
|
|
|
430
|
+
const prefix = String(sourcePrefix || "");
|
|
347
431
|
return `
|
|
348
|
-
:resolve-lookup-items="resolveLookupItems"
|
|
349
|
-
:resolve-lookup-loading="resolveLookupLoading"
|
|
350
|
-
:resolve-lookup-search="resolveLookupSearch"
|
|
351
|
-
:set-lookup-search="setLookupSearch"`;
|
|
432
|
+
:resolve-lookup-items="${prefix}resolveLookupItems"
|
|
433
|
+
:resolve-lookup-loading="${prefix}resolveLookupLoading"
|
|
434
|
+
:resolve-lookup-search="${prefix}resolveLookupSearch"
|
|
435
|
+
:set-lookup-search="${prefix}setLookupSearch"`;
|
|
352
436
|
}
|
|
353
437
|
|
|
354
438
|
function buildLookupFormPropDefinitions({ createFields = [], editFields = [] } = {}) {
|
|
@@ -468,6 +552,16 @@ function resolveTargetRootRelativeRoutePath(pageTarget = {}) {
|
|
|
468
552
|
return visibleRouteSegments.length > 0 ? `/${visibleRouteSegments.join("/")}` : "/";
|
|
469
553
|
}
|
|
470
554
|
|
|
555
|
+
function resolveNavigationInferenceRoutePath(pageTarget = {}) {
|
|
556
|
+
const visibleRouteSegments = Array.isArray(pageTarget?.visibleRouteSegments)
|
|
557
|
+
? pageTarget.visibleRouteSegments.map((entry) => normalizeText(entry)).filter(Boolean)
|
|
558
|
+
: [];
|
|
559
|
+
if (visibleRouteSegments.length > 0) {
|
|
560
|
+
return `/${visibleRouteSegments.join("/")}`;
|
|
561
|
+
}
|
|
562
|
+
return String(pageTarget?.routeUrlSuffix || "");
|
|
563
|
+
}
|
|
564
|
+
|
|
471
565
|
function resolveMenuToPropLine(linkTo = "") {
|
|
472
566
|
if (!linkTo) {
|
|
473
567
|
return "";
|
|
@@ -483,6 +577,44 @@ function resolveMenuOwnerLine(owner = "") {
|
|
|
483
577
|
return ` owner: ${JSON.stringify(normalizedOwner)},\n`;
|
|
484
578
|
}
|
|
485
579
|
|
|
580
|
+
function buildMenuAppendBlock({
|
|
581
|
+
hasListOperation = false,
|
|
582
|
+
menuMarker = "",
|
|
583
|
+
pageLinkTarget = null,
|
|
584
|
+
pageTarget = {}
|
|
585
|
+
} = {}) {
|
|
586
|
+
const placementId = String(pageLinkTarget?.pageTarget?.placementId || "");
|
|
587
|
+
const placementTarget = String(pageLinkTarget?.placementTarget?.id || "");
|
|
588
|
+
if (!hasListOperation || !placementId || !placementTarget) {
|
|
589
|
+
return "";
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
const surfaceId = String(pageTarget?.surfaceId || "");
|
|
593
|
+
const routeUrlSuffix = String(pageLinkTarget?.pageTarget?.routeUrlSuffix || "");
|
|
594
|
+
const menuToPropLine = resolveMenuToPropLine(pageLinkTarget?.linkTo || "");
|
|
595
|
+
const whenLine = String(pageLinkTarget?.whenLine || "");
|
|
596
|
+
|
|
597
|
+
return `
|
|
598
|
+
// ${menuMarker}
|
|
599
|
+
{
|
|
600
|
+
addPlacement({
|
|
601
|
+
id: ${JSON.stringify(placementId)},
|
|
602
|
+
target: ${JSON.stringify(placementTarget)},
|
|
603
|
+
${resolveMenuOwnerLine(pageLinkTarget?.placementTarget?.owner || "")} kind: "link",
|
|
604
|
+
surfaces: [${JSON.stringify(surfaceId)}],
|
|
605
|
+
order: 155,
|
|
606
|
+
props: {
|
|
607
|
+
label: ${JSON.stringify(pageTarget?.defaultName || "")},
|
|
608
|
+
icon: ${JSON.stringify(DEFAULT_GENERATED_LINK_ICON)},
|
|
609
|
+
surface: ${JSON.stringify(surfaceId)},
|
|
610
|
+
scopedSuffix: ${JSON.stringify(routeUrlSuffix)},
|
|
611
|
+
unscopedSuffix: ${JSON.stringify(routeUrlSuffix)},
|
|
612
|
+
${menuToPropLine} },
|
|
613
|
+
${whenLine} });
|
|
614
|
+
}
|
|
615
|
+
`;
|
|
616
|
+
}
|
|
617
|
+
|
|
486
618
|
function resolveCrudRelativePath(namespace = "") {
|
|
487
619
|
return `/${requireCrudNamespace(namespace, {
|
|
488
620
|
context: "crud-ui-generator resource namespace"
|
|
@@ -509,7 +641,7 @@ function buildListHeadingTitleSetup({
|
|
|
509
641
|
}
|
|
510
642
|
|
|
511
643
|
return `const parentTitle = useCrudListParentTitle({
|
|
512
|
-
listRuntime
|
|
644
|
+
listRuntime,
|
|
513
645
|
resource: uiResource,
|
|
514
646
|
adapter: UI_OPERATION_ADAPTER || undefined,
|
|
515
647
|
recordIdParam: UI_RECORD_ID_PARAM,
|
|
@@ -651,13 +783,20 @@ async function buildUiTemplateContext({ appRoot, options } = {}) {
|
|
|
651
783
|
const viewTitleFallbackFieldKey = hasViewOperation
|
|
652
784
|
? resolveViewTitleFallbackFieldKey(viewFieldsAll, { recordIdFieldKey })
|
|
653
785
|
: "";
|
|
786
|
+
const listTitleFallbackFieldKey = hasListOperation
|
|
787
|
+
? resolveViewTitleFallbackFieldKey(listFieldsAll, { recordIdFieldKey: listRecordIdFieldKey })
|
|
788
|
+
: "";
|
|
654
789
|
|
|
655
|
-
const pageLinkTarget = hasListOperation
|
|
790
|
+
const pageLinkTarget = hasListOperation && shouldCreateNavigationLink(options, {
|
|
791
|
+
routePath: resolveNavigationInferenceRoutePath(pageTarget)
|
|
792
|
+
})
|
|
656
793
|
? await resolvePageLinkTargetDetails({
|
|
657
794
|
appRoot,
|
|
658
795
|
pageTarget,
|
|
659
796
|
targetFile: listTargetFile,
|
|
660
|
-
placement: options
|
|
797
|
+
placement: resolveNavigationRoleLinkPlacement(options, {
|
|
798
|
+
routePath: resolveNavigationInferenceRoutePath(pageTarget)
|
|
799
|
+
}),
|
|
661
800
|
context: "crud-ui-generator"
|
|
662
801
|
})
|
|
663
802
|
: null;
|
|
@@ -666,6 +805,8 @@ async function buildUiTemplateContext({ appRoot, options } = {}) {
|
|
|
666
805
|
: "";
|
|
667
806
|
const createFormColumns = buildFormColumns(createFields);
|
|
668
807
|
const editFormColumns = buildFormColumns(editFields);
|
|
808
|
+
const sharedFormFields = Object.freeze([...createFields, ...editFields]);
|
|
809
|
+
const listCopy = buildCrudListCopy(resourceLabels);
|
|
669
810
|
|
|
670
811
|
return {
|
|
671
812
|
__JSKIT_UI_RESOURCE_IMPORT_PATH__: `/${normalizeRelativeAppPath(options?.["resource-file"])}`,
|
|
@@ -674,6 +815,11 @@ async function buildUiTemplateContext({ appRoot, options } = {}) {
|
|
|
674
815
|
__JSKIT_UI_RESOURCE_NAMESPACE__: resourceNamespace,
|
|
675
816
|
__JSKIT_UI_RESOURCE_SINGULAR_TITLE__: resourceLabels.singularTitle,
|
|
676
817
|
__JSKIT_UI_RESOURCE_PLURAL_TITLE__: resourceLabels.pluralTitle,
|
|
818
|
+
__JSKIT_UI_LIST_LOAD_ERROR_TITLE__: listCopy.loadErrorTitle,
|
|
819
|
+
__JSKIT_UI_LIST_LOAD_ERROR_BODY__: listCopy.loadErrorBody,
|
|
820
|
+
__JSKIT_UI_LIST_EMPTY_TITLE__: listCopy.emptyTitle,
|
|
821
|
+
__JSKIT_UI_LIST_EMPTY_BODY__: listCopy.emptyBody,
|
|
822
|
+
__JSKIT_UI_LIST_CREATE_LABEL__: listCopy.createLabel,
|
|
677
823
|
__JSKIT_UI_ROUTE_TITLE__: pageTarget.defaultName,
|
|
678
824
|
__JSKIT_UI_PARENT_TITLE_MODE__: parentTitleMode,
|
|
679
825
|
__JSKIT_UI_LIST_PARENT_TITLE_IMPORT_LINE__: buildListParentTitleImportLine(parentTitleMode),
|
|
@@ -687,6 +833,10 @@ async function buildUiTemplateContext({ appRoot, options } = {}) {
|
|
|
687
833
|
__JSKIT_UI_SURFACE_ID__: pageTarget.surfaceId,
|
|
688
834
|
__JSKIT_UI_LIST_HEADER_COLUMNS__: buildListHeaderColumns(listFields),
|
|
689
835
|
__JSKIT_UI_LIST_ROW_COLUMNS__: buildListRowColumns(listFields),
|
|
836
|
+
__JSKIT_UI_LIST_CARD_FIELDS__: buildListCardFields(listFields),
|
|
837
|
+
__JSKIT_UI_LIST_CARD_SLOT_PROPS__: buildListCardSlotProps(listFields),
|
|
838
|
+
__JSKIT_UI_LIST_ROW_SLOT_PROPS__: buildListRowSlotProps(listFields),
|
|
839
|
+
__JSKIT_UI_LIST_TITLE_FALLBACK_FIELD_KEY__: JSON.stringify(listTitleFallbackFieldKey),
|
|
690
840
|
__JSKIT_UI_LIST_REALTIME_EVENTS__: JSON.stringify(listRealtimeEvents),
|
|
691
841
|
__JSKIT_UI_LIST_RECORD_ID_EXPR__: resolveRecordIdExpression(recordIdFields),
|
|
692
842
|
__JSKIT_UI_VIEW_COLUMNS__: buildViewColumns(viewFields),
|
|
@@ -709,6 +859,9 @@ async function buildUiTemplateContext({ appRoot, options } = {}) {
|
|
|
709
859
|
__JSKIT_UI_EDIT_FORM_COLUMNS__: editFormColumns,
|
|
710
860
|
__JSKIT_UI_CREATE_FORM_COLUMNS_DIRECT__: rewriteGeneratedBlockIndent(createFormColumns, { trimPrefix: " " }),
|
|
711
861
|
__JSKIT_UI_EDIT_FORM_COLUMNS_DIRECT__: rewriteGeneratedBlockIndent(editFormColumns, { trimPrefix: " " }),
|
|
862
|
+
__JSKIT_UI_CREATE_FORM_SLOT_PROPS__: buildCrudFieldsSlotProps(createFields),
|
|
863
|
+
__JSKIT_UI_EDIT_FORM_SLOT_PROPS__: buildCrudFieldsSlotProps(editFields),
|
|
864
|
+
__JSKIT_UI_SHARED_FORM_SLOT_PROPS__: buildCrudFieldsSlotProps(sharedFormFields, { includeMode: true }),
|
|
712
865
|
__JSKIT_UI_CREATE_FORM_FIELDS__: JSON.stringify(createFields),
|
|
713
866
|
__JSKIT_UI_EDIT_FORM_FIELDS__: JSON.stringify(editFields),
|
|
714
867
|
__JSKIT_UI_CREATE_FORM_FIELD_PUSH_LINES__: renderObjectPushLines("UI_CREATE_FORM_FIELDS", createFields),
|
|
@@ -727,6 +880,7 @@ async function buildUiTemplateContext({ appRoot, options } = {}) {
|
|
|
727
880
|
}),
|
|
728
881
|
__JSKIT_UI_CREATE_LOOKUP_FORM_PROPS__: buildLookupFormProps(createFields),
|
|
729
882
|
__JSKIT_UI_EDIT_LOOKUP_FORM_PROPS__: buildLookupFormProps(editFields),
|
|
883
|
+
__JSKIT_UI_SHARED_LOOKUP_FORM_PROPS__: buildLookupFormProps(sharedFormFields, { sourcePrefix: "props." }),
|
|
730
884
|
__JSKIT_UI_FORM_LOOKUP_PROP_DEFS__: buildLookupFormPropDefinitions({
|
|
731
885
|
createFields,
|
|
732
886
|
editFields
|
|
@@ -740,7 +894,13 @@ async function buildUiTemplateContext({ appRoot, options } = {}) {
|
|
|
740
894
|
__JSKIT_UI_MENU_NON_WORKSPACE_SUFFIX__: String(pageLinkTarget?.pageTarget?.routeUrlSuffix || ""),
|
|
741
895
|
__JSKIT_UI_MENU_WHEN_LINE__: String(pageLinkTarget?.whenLine || ""),
|
|
742
896
|
__JSKIT_UI_MENU_TO_PROP_LINE__: resolveMenuToPropLine(pageLinkTarget?.linkTo || ""),
|
|
743
|
-
__JSKIT_UI_MENU_LABEL__: pageTarget.defaultName
|
|
897
|
+
__JSKIT_UI_MENU_LABEL__: pageTarget.defaultName,
|
|
898
|
+
__JSKIT_UI_MENU_APPEND_BLOCK__: buildMenuAppendBlock({
|
|
899
|
+
hasListOperation,
|
|
900
|
+
menuMarker,
|
|
901
|
+
pageLinkTarget,
|
|
902
|
+
pageTarget
|
|
903
|
+
})
|
|
744
904
|
};
|
|
745
905
|
}
|
|
746
906
|
|
|
@@ -835,7 +835,7 @@ function renderTemplateJsStringLiteral(value) {
|
|
|
835
835
|
|
|
836
836
|
function buildListHeaderColumns(fields = []) {
|
|
837
837
|
return (Array.isArray(fields) ? fields : [])
|
|
838
|
-
.map((field) => `
|
|
838
|
+
.map((field) => ` <th>${escapeHtml(field.label)}</th>`)
|
|
839
839
|
.join("\n");
|
|
840
840
|
}
|
|
841
841
|
|
|
@@ -888,10 +888,25 @@ function buildListRowColumns(fields = []) {
|
|
|
888
888
|
return (Array.isArray(fields) ? fields : [])
|
|
889
889
|
.map((field) => {
|
|
890
890
|
if (isLookupField(field)) {
|
|
891
|
-
return `
|
|
891
|
+
return ` <td>{{ records.resolveFieldDisplay(record, ${toLookupDisplayFieldExpression(field)}) }}</td>`;
|
|
892
892
|
}
|
|
893
893
|
|
|
894
|
-
return `
|
|
894
|
+
return ` <td>{{ ${toAccessorExpression("record", field.key)} }}</td>`;
|
|
895
|
+
})
|
|
896
|
+
.join("\n");
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
function buildListCardFields(fields = []) {
|
|
900
|
+
return (Array.isArray(fields) ? fields : [])
|
|
901
|
+
.map((field) => {
|
|
902
|
+
const valueExpression = isLookupField(field)
|
|
903
|
+
? `records.resolveFieldDisplay(record, ${toLookupDisplayFieldExpression(field)})`
|
|
904
|
+
: toAccessorExpression("record", field.key);
|
|
905
|
+
|
|
906
|
+
return ` <div class="ui-generator-list-card__field">
|
|
907
|
+
<span class="ui-generator-list-card__field-label">${escapeHtml(field.label)}</span>
|
|
908
|
+
<span class="ui-generator-list-card__field-value">{{ formatListCardValue(${valueExpression}) }}</span>
|
|
909
|
+
</div>`;
|
|
895
910
|
})
|
|
896
911
|
.join("\n");
|
|
897
912
|
}
|
|
@@ -903,10 +918,10 @@ function buildViewColumns(fields = []) {
|
|
|
903
918
|
? `view.resolveFieldDisplay(view.record, ${toLookupDisplayFieldExpression(field)})`
|
|
904
919
|
: toOptionalAccessorExpression("view.record", field.key);
|
|
905
920
|
|
|
906
|
-
return `
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
921
|
+
return ` <v-col cols="12" md="6">
|
|
922
|
+
<div class="text-caption text-medium-emphasis">${escapeHtml(field.label)}</div>
|
|
923
|
+
<div class="text-body-1">{{ ${valueExpression} }}</div>
|
|
924
|
+
</v-col>`;
|
|
910
925
|
})
|
|
911
926
|
.join("\n");
|
|
912
927
|
}
|
|
@@ -929,34 +944,34 @@ function buildFormColumns(fields = []) {
|
|
|
929
944
|
const fieldErrorExpression = `resolveFieldErrors(${fieldKeyLiteral})`;
|
|
930
945
|
const component = normalizeText(field?.component).toLowerCase();
|
|
931
946
|
if (component === "switch") {
|
|
932
|
-
return `
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
947
|
+
return ` <v-col cols="12" md="6">
|
|
948
|
+
<v-switch
|
|
949
|
+
v-model="${formAccessor}"
|
|
950
|
+
label="${label}"
|
|
951
|
+
color="primary"
|
|
952
|
+
hide-details="auto"
|
|
953
|
+
:disabled="addEdit.isFieldLocked"
|
|
954
|
+
:error-messages="${fieldErrorExpression}"
|
|
955
|
+
/>
|
|
956
|
+
</v-col>`;
|
|
942
957
|
}
|
|
943
958
|
|
|
944
959
|
if (component === "select") {
|
|
945
960
|
const selectOptions = Array.isArray(field?.options) ? field.options : [];
|
|
946
|
-
return `
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
961
|
+
return ` <v-col cols="12" md="6">
|
|
962
|
+
<v-select
|
|
963
|
+
v-model="${formAccessor}"
|
|
964
|
+
label="${label}"
|
|
965
|
+
variant="outlined"
|
|
966
|
+
density="comfortable"
|
|
967
|
+
:items="${serializeTemplateBindingValue(selectOptions)}"
|
|
968
|
+
item-title="label"
|
|
969
|
+
item-value="value"
|
|
970
|
+
:disabled="addEdit.isFieldLocked"
|
|
971
|
+
:clearable="${field.nullable === true ? "true" : "false"}"
|
|
972
|
+
:error-messages="${fieldErrorExpression}"
|
|
973
|
+
/>
|
|
974
|
+
</v-col>`;
|
|
960
975
|
}
|
|
961
976
|
|
|
962
977
|
if (component === "lookup") {
|
|
@@ -964,53 +979,53 @@ function buildFormColumns(fields = []) {
|
|
|
964
979
|
const useAutocomplete = lookupFormControl !== "select";
|
|
965
980
|
const lookupComponentTag = useAutocomplete ? "v-autocomplete" : "v-select";
|
|
966
981
|
const lookupAttributeLines = [
|
|
967
|
-
`
|
|
982
|
+
` :items="fieldLookupItems(${fieldKeyLiteral}, { selectedValue: ${formAccessor}, selectedRecord: addEdit.resource.data })"`
|
|
968
983
|
];
|
|
969
984
|
if (useAutocomplete) {
|
|
970
985
|
lookupAttributeLines.push(
|
|
971
|
-
`
|
|
972
|
-
`
|
|
986
|
+
` :search="fieldLookupSearch(${fieldKeyLiteral})"`,
|
|
987
|
+
` @update:search="setFieldLookupSearch(${fieldKeyLiteral}, $event)"`
|
|
973
988
|
);
|
|
974
989
|
}
|
|
975
990
|
lookupAttributeLines.push(
|
|
976
|
-
`
|
|
977
|
-
`
|
|
991
|
+
` item-title="label"`,
|
|
992
|
+
` item-value="value"`
|
|
978
993
|
);
|
|
979
994
|
if (useAutocomplete) {
|
|
980
|
-
lookupAttributeLines.push("
|
|
995
|
+
lookupAttributeLines.push(" no-filter");
|
|
981
996
|
}
|
|
982
|
-
return `
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
997
|
+
return ` <v-col cols="12" md="6">
|
|
998
|
+
<${lookupComponentTag}
|
|
999
|
+
v-model="${formAccessor}"
|
|
1000
|
+
label="${label}"
|
|
1001
|
+
variant="outlined"
|
|
1002
|
+
density="comfortable"
|
|
1003
|
+
autocomplete="off"
|
|
989
1004
|
${lookupAttributeLines.join("\n")}
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
1005
|
+
:loading="fieldLookupLoading(${fieldKeyLiteral})"
|
|
1006
|
+
:disabled="addEdit.isFieldLocked"
|
|
1007
|
+
:clearable="${field.nullable === true ? "true" : "false"}"
|
|
1008
|
+
:error-messages="${fieldErrorExpression}"
|
|
1009
|
+
/>
|
|
1010
|
+
</v-col>`;
|
|
996
1011
|
}
|
|
997
1012
|
|
|
998
1013
|
const inputType = normalizeText(field?.inputType) || "text";
|
|
999
1014
|
const maxLength = Number.isInteger(field?.maxLength) && field.maxLength > 0
|
|
1000
1015
|
? String(field.maxLength)
|
|
1001
1016
|
: "undefined";
|
|
1002
|
-
return `
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1017
|
+
return ` <v-col cols="12" md="6">
|
|
1018
|
+
<v-text-field
|
|
1019
|
+
v-model="${formAccessor}"
|
|
1020
|
+
label="${label}"
|
|
1021
|
+
type="${escapeHtml(inputType)}"
|
|
1022
|
+
variant="outlined"
|
|
1023
|
+
density="comfortable"
|
|
1024
|
+
:maxlength="${maxLength}"
|
|
1025
|
+
:readonly="addEdit.isFieldLocked"
|
|
1026
|
+
:error-messages="${fieldErrorExpression}"
|
|
1027
|
+
/>
|
|
1028
|
+
</v-col>`;
|
|
1014
1029
|
})
|
|
1015
1030
|
.filter(Boolean)
|
|
1016
1031
|
.join("\n");
|
|
@@ -1069,6 +1084,7 @@ export {
|
|
|
1069
1084
|
resolveNearestParentRouteParamKey,
|
|
1070
1085
|
buildListHeaderColumns,
|
|
1071
1086
|
buildListRowColumns,
|
|
1087
|
+
buildListCardFields,
|
|
1072
1088
|
buildViewColumns,
|
|
1073
1089
|
buildFormColumns,
|
|
1074
1090
|
resolveRecordIdFieldKey,
|