@olenbetong/appframe-core 1.0.3 → 1.2.0

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.
@@ -0,0 +1,11 @@
1
+ export declare type DownloadItem = {
2
+ status: "cancelled" | "error" | "requested" | "downloading" | "downloaded" | "completed";
3
+ bytesReceived?: number;
4
+ error?: string;
5
+ };
6
+ export declare type DownloadParameters = {
7
+ controller?: AbortController;
8
+ fileName: string;
9
+ onProgress?: (progress: DownloadItem) => void;
10
+ };
11
+ export declare function downloadRequest(request: Request, { fileName, controller, onProgress }: DownloadParameters): Promise<void>;
@@ -0,0 +1,53 @@
1
+ import { downloadBlob } from "./downloadBlob.js";
2
+ export async function downloadRequest(request, { fileName, controller, onProgress }) {
3
+ function reportProgress(progress) {
4
+ if (typeof onProgress === "function") {
5
+ onProgress({ ...progress });
6
+ }
7
+ }
8
+ let bytesReceived = 0;
9
+ reportProgress({ status: "requested" });
10
+ try {
11
+ let response = await fetch(request);
12
+ if (!response.ok || !response.body) {
13
+ throw new Error(response.status + " " + response.statusText);
14
+ }
15
+ try {
16
+ const reader = response.body.getReader();
17
+ const stream = new ReadableStream({
18
+ start: function (controller) {
19
+ function pump() {
20
+ return reader.read().then(function (read) {
21
+ if (read.done) {
22
+ controller.close();
23
+ return;
24
+ }
25
+ bytesReceived += read.value.length;
26
+ reportProgress({ bytesReceived, status: "downloading" });
27
+ controller.enqueue(read.value);
28
+ return pump();
29
+ });
30
+ }
31
+ return pump();
32
+ },
33
+ });
34
+ response = new Response(stream);
35
+ }
36
+ catch (error) {
37
+ // eslint-disable-next-line no-console
38
+ console.warn("Failed to replace response stream to log download progress.");
39
+ }
40
+ const blob = await response.blob();
41
+ reportProgress({ bytesReceived, status: "downloaded" });
42
+ await downloadBlob(blob, fileName);
43
+ reportProgress({ bytesReceived, status: "completed" });
44
+ }
45
+ catch (err) {
46
+ if (controller && controller.signal && controller.signal.aborted) {
47
+ reportProgress({ status: "cancelled" });
48
+ }
49
+ else {
50
+ reportProgress({ error: err.message, status: "error" });
51
+ }
52
+ }
53
+ }
@@ -0,0 +1,89 @@
1
+ import { DataObject, DataObjectOptions } from "@olenbetong/appframe-data";
2
+ import { RetrieveParameters } from "@olenbetong/appframe-data";
3
+ export declare type DataExportColumn = {
4
+ name: string;
5
+ caption?: string;
6
+ cellFormat?: string;
7
+ format?: string;
8
+ };
9
+ export declare type DataExportOptions = RetrieveParameters<any> & {
10
+ columns: DataExportColumn[];
11
+ };
12
+ export declare type DataObjectReportParams = {
13
+ articleId: string;
14
+ controller?: AbortController;
15
+ dataObject: string;
16
+ format: "pdf" | "xlsx" | "csv" | "xls";
17
+ fileName?: string;
18
+ options: DataExportOptions;
19
+ id?: string;
20
+ title?: string;
21
+ subTitle?: string;
22
+ /**
23
+ * Whether the title should be shown in the exported file (default true)
24
+ */
25
+ includeTitle?: boolean;
26
+ /**
27
+ * Whether the filter should be shown in the exported file (default true)
28
+ */
29
+ includefilter?: boolean;
30
+ /**
31
+ * Whether we should try to convert from camelCase to human readable names (default true)
32
+ */
33
+ formatHeaders?: boolean;
34
+ /**
35
+ * Whether the header should be frozen on top of the excel file.
36
+ */
37
+ freezeHeader?: boolean;
38
+ };
39
+ /**
40
+ * Get columns and retrieve parameters from a data object to be used to create a report.
41
+ * @param dataObject Data object to get parameters and columns from
42
+ * @param specifiedOptions Options to override the data object options
43
+ * @returns
44
+ */
45
+ export declare function exportOptionsFromDataObject(dataObject: DataObject<any>, specifiedOptions: Partial<DataObjectOptions<any>>): {
46
+ allowDelete?: boolean | undefined;
47
+ allowInsert?: boolean | undefined;
48
+ allowUpdate?: boolean | undefined;
49
+ articleId?: string | undefined;
50
+ dataHandler?: import("@olenbetong/appframe-data").DataHandlerDescriptor | import("@olenbetong/appframe-data").DataHandler<any> | undefined;
51
+ dataSourceId?: string | undefined;
52
+ disableAutoload?: boolean | undefined;
53
+ dynamicLoading?: boolean | undefined;
54
+ errorHandler?: ((error: string, callback?: Function | undefined) => void) | undefined;
55
+ fields?: import("@olenbetong/appframe-data").FieldDefinition[] | undefined;
56
+ groupBy?: string | number | symbol | string[] | null | undefined;
57
+ hasIDTrig?: boolean | undefined;
58
+ hasIITrig?: boolean | undefined;
59
+ hasIUTrig?: boolean | undefined;
60
+ linkFields?: (string | number | symbol)[] | Record<string | number | symbol, string> | null | undefined;
61
+ masterDataObject?: import("@olenbetong/appframe-data").AfDataObject<any> | null | undefined;
62
+ optimisticLocking?: boolean | undefined;
63
+ parameters?: RetrieveParameters<any> | undefined;
64
+ selectFirstRow?: boolean | undefined;
65
+ systemFieldNames?: Record<string, string | number | symbol> | undefined;
66
+ strict?: boolean | undefined;
67
+ timeout?: number | undefined;
68
+ uniqueIdField?: string | number | symbol | undefined;
69
+ columns: {
70
+ name: string;
71
+ }[];
72
+ distinctRows: boolean | undefined;
73
+ filterObject: import("@olenbetong/appframe-data").Filter | undefined;
74
+ filterString: string | undefined;
75
+ maxRecords: number | undefined;
76
+ sortOrder: Record<string | number | symbol, import("@olenbetong/appframe-data").SortOrder>[] | undefined;
77
+ whereClause: string | undefined;
78
+ whereObject: import("@olenbetong/appframe-data").Filter | undefined;
79
+ };
80
+ /**
81
+ * Get a request object to download a report from a data object.
82
+ * @param params Parameters to use to create a report
83
+ * @returns Request object for the report
84
+ */
85
+ export declare function getDataObjectReportRequest(params: DataObjectReportParams): {
86
+ controller: AbortController;
87
+ request: Request;
88
+ };
89
+ export declare function openDataObjectReport(params: DataObjectReportParams): Promise<void>;
@@ -0,0 +1,61 @@
1
+ import { downloadRequest } from "./downloadRequest.js";
2
+ /**
3
+ * Get columns and retrieve parameters from a data object to be used to create a report.
4
+ * @param dataObject Data object to get parameters and columns from
5
+ * @param specifiedOptions Options to override the data object options
6
+ * @returns
7
+ */
8
+ export function exportOptionsFromDataObject(dataObject, specifiedOptions) {
9
+ return {
10
+ columns: dataObject.getFields().map((field) => ({ name: field.name })),
11
+ distinctRows: dataObject.getParameter("distinctRows"),
12
+ filterObject: dataObject.getParameter("filterObject"),
13
+ filterString: dataObject.getParameter("filterString"),
14
+ maxRecords: dataObject.getParameter("maxRecords"),
15
+ sortOrder: dataObject.getParameter("sortOrder"),
16
+ whereClause: dataObject.getParameter("whereClause"),
17
+ whereObject: dataObject.getParameter("whereObject"),
18
+ ...specifiedOptions,
19
+ };
20
+ }
21
+ const defaultOptions = {
22
+ columns: [],
23
+ distinctRows: false,
24
+ filterObject: null,
25
+ filterString: "",
26
+ maxRecords: -1,
27
+ sortOrder: [],
28
+ whereClause: "",
29
+ whereObject: null,
30
+ };
31
+ /**
32
+ * Get a request object to download a report from a data object.
33
+ * @param params Parameters to use to create a report
34
+ * @returns Request object for the report
35
+ */
36
+ export function getDataObjectReportRequest(params) {
37
+ const { articleId, dataObject, format, options } = params;
38
+ const reportOptions = options ? Object.assign({}, defaultOptions, options) : defaultOptions;
39
+ const body = "data=" + encodeURIComponent(JSON.stringify(reportOptions));
40
+ const requestUrl = `/api/data-object/export/${articleId}/${dataObject}/${format}`;
41
+ const controller = params.controller ?? new AbortController();
42
+ return {
43
+ controller,
44
+ request: new Request(requestUrl, {
45
+ body,
46
+ credentials: "same-origin",
47
+ headers: {
48
+ "Content-Type": "application/x-www-form-urlencoded",
49
+ },
50
+ method: "POST",
51
+ signal: controller.signal,
52
+ }),
53
+ };
54
+ }
55
+ export function openDataObjectReport(params) {
56
+ const { dataObject, fileName, format } = params;
57
+ const { request } = getDataObjectReportRequest(params);
58
+ return downloadRequest(request, {
59
+ fileName: `${fileName || dataObject}.${format}`,
60
+ });
61
+ }
@@ -0,0 +1,26 @@
1
+ import type { DataObject, Procedure } from "@olenbetong/appframe-data";
2
+ /**
3
+ * Creates an object from form data. Uses the type map passed to convert the form values to the correct type.
4
+ * @param form HTML form element to get form data from.
5
+ * @param typeMap A mapping from field name to field type
6
+ * @returns
7
+ */
8
+ export declare function formDataToObject<T = any>(form: HTMLFormElement, typeMap?: Record<string, string>): Partial<T>;
9
+ /**
10
+ * Creates a partial data object record from form data. The name property is mapped
11
+ * to field name.
12
+ *
13
+ * @param form HTML form element to get FormData from
14
+ * @param dataObject Data object for which to create a record from the form data
15
+ * @returns {Partial<T>} Partial data object record
16
+ */
17
+ export declare function formDataToDataObjectRecord<T>(form: HTMLFormElement, dataObject: DataObject<T>): Partial<T>;
18
+ /**
19
+ * Creates procedure parameters from form data. The name property is mapped
20
+ * to parameter names.
21
+ *
22
+ * @param form HTML Form element to get FormData from
23
+ * @param procedure Appframe Procedure to create parameters for
24
+ * @returns
25
+ */
26
+ export declare function formDataToProcedureParameters<T>(form: HTMLFormElement, procedure: Procedure<T, any>): T;
@@ -0,0 +1,73 @@
1
+ function getFormValue(value, type) {
2
+ if (value === "") {
3
+ return null;
4
+ }
5
+ else if (type === "number") {
6
+ let numValue = Number(value);
7
+ return isNaN(numValue) ? undefined : numValue;
8
+ }
9
+ else if (["date", "datetime", "time"].includes(type)) {
10
+ let dateValue = new Date(value);
11
+ return isNaN(dateValue.getTime()) ? undefined : dateValue;
12
+ }
13
+ else if (type === "boolean") {
14
+ return value.toLowerCase() === "true";
15
+ }
16
+ else if (type === "bigint") {
17
+ return BigInt(value);
18
+ }
19
+ return value;
20
+ }
21
+ /**
22
+ * Creates an object from form data. Uses the type map passed to convert the form values to the correct type.
23
+ * @param form HTML form element to get form data from.
24
+ * @param typeMap A mapping from field name to field type
25
+ * @returns
26
+ */
27
+ export function formDataToObject(form, typeMap = {}) {
28
+ let formData = new FormData(form);
29
+ let result = {};
30
+ for (let entry of formData.entries()) {
31
+ let field = entry[0];
32
+ let type = typeMap?.[field] ?? "string";
33
+ let value = getFormValue(entry[1], type);
34
+ if (result.hasOwnProperty(field)) {
35
+ if (Array.isArray(result[field])) {
36
+ result[field].push(value);
37
+ }
38
+ else {
39
+ result[field] = [result[field], value];
40
+ }
41
+ }
42
+ else {
43
+ result[field] = value;
44
+ }
45
+ }
46
+ return result;
47
+ }
48
+ /**
49
+ * Creates a partial data object record from form data. The name property is mapped
50
+ * to field name.
51
+ *
52
+ * @param form HTML form element to get FormData from
53
+ * @param dataObject Data object for which to create a record from the form data
54
+ * @returns {Partial<T>} Partial data object record
55
+ */
56
+ export function formDataToDataObjectRecord(form, dataObject) {
57
+ let fields = dataObject.getFields();
58
+ let types = Object.fromEntries(fields.map((field) => [field.name, field.type]));
59
+ return formDataToObject(form, types);
60
+ }
61
+ /**
62
+ * Creates procedure parameters from form data. The name property is mapped
63
+ * to parameter names.
64
+ *
65
+ * @param form HTML Form element to get FormData from
66
+ * @param procedure Appframe Procedure to create parameters for
67
+ * @returns
68
+ */
69
+ export function formDataToProcedureParameters(form, procedure) {
70
+ let parameters = procedure.getParameters();
71
+ let types = Object.fromEntries(parameters.map((param) => [param.name, param.type]));
72
+ return formDataToObject(form, types);
73
+ }
package/es/index.d.ts CHANGED
@@ -1,8 +1,12 @@
1
1
  export { default as mitt } from "mitt";
2
+ export { downloadBlob } from "./downloadBlob.js";
3
+ export * from "./downloadRequest.js";
4
+ export * from "./exportDataObject.js";
2
5
  export { default as expose } from "./expose.js";
3
6
  export { getLocalizedString } from "./getLocalizedString.js";
4
7
  export { default as localStorage } from "./localStorage.js";
5
8
  export { default as SessionHandler } from "./SessionHandler.js";
6
9
  export { default as UserSession } from "./UserSession.js";
10
+ export { formDataToDataObjectRecord, formDataToObject, formDataToProcedureParameters } from "./formDataToObject.js";
7
11
  export * from "./settings.js";
8
12
  export declare const version: string;
package/es/index.js CHANGED
@@ -1,8 +1,12 @@
1
1
  export { default as mitt } from "mitt";
2
+ export { downloadBlob } from "./downloadBlob.js";
3
+ export * from "./downloadRequest.js";
4
+ export * from "./exportDataObject.js";
2
5
  export { default as expose } from "./expose.js";
3
6
  export { getLocalizedString } from "./getLocalizedString.js";
4
7
  export { default as localStorage } from "./localStorage.js";
5
8
  export { default as SessionHandler } from "./SessionHandler.js";
6
9
  export { default as UserSession } from "./UserSession.js";
10
+ export { formDataToDataObjectRecord, formDataToObject, formDataToProcedureParameters } from "./formDataToObject.js";
7
11
  export * from "./settings.js";
8
- export const version = "1.0.3";
12
+ export const version = "1.2.0";
package/es/warn.js CHANGED
@@ -1,12 +1,4 @@
1
1
  /* eslint-disable no-console */
2
2
  export default function warn(message, fallbackToAlert) {
3
- if (console && typeof console.warn === "function") {
4
- console.warn(message);
5
- }
6
- else if (console && typeof console.log === "function") {
7
- console.log(message);
8
- }
9
- else if (fallbackToAlert) {
10
- alert(message);
11
- }
3
+ console.warn(message);
12
4
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@olenbetong/appframe-core",
3
- "version": "1.0.3",
3
+ "version": "1.2.0",
4
4
  "description": "Javascript libraries to utilize the Appframe framework",
5
5
  "type": "module",
6
6
  "main": "./es/index.js",
@@ -35,5 +35,5 @@
35
35
  "devDependencies": {
36
36
  "chalk": "^5.0.1"
37
37
  },
38
- "gitHead": "4770ac0781b1f3e04b4843a4ae85976cdb2b59ae"
38
+ "gitHead": "dce88dc39a661d6d192fdae09128f958360def81"
39
39
  }
@@ -0,0 +1,21 @@
1
+ export function downloadBlob(blob: Blob | MediaSource, fileName: string) {
2
+ return new Promise<void>(function (resolve, reject) {
3
+ try {
4
+ const url = URL.createObjectURL(blob);
5
+ const parent = document.body;
6
+ const anchor = document.createElement("a");
7
+ anchor.setAttribute("download", fileName);
8
+ anchor.href = url;
9
+ parent.append(anchor);
10
+ anchor.click();
11
+ parent.removeChild(anchor);
12
+
13
+ setTimeout(function () {
14
+ URL.revokeObjectURL(url);
15
+ resolve();
16
+ }, 250);
17
+ } catch (ex) {
18
+ reject(ex);
19
+ }
20
+ });
21
+ }
@@ -0,0 +1,76 @@
1
+ import { downloadBlob } from "./downloadBlob.js";
2
+
3
+ export type DownloadItem = {
4
+ status: "cancelled" | "error" | "requested" | "downloading" | "downloaded" | "completed";
5
+ bytesReceived?: number;
6
+ error?: string;
7
+ };
8
+
9
+ export type DownloadParameters = {
10
+ controller?: AbortController;
11
+ fileName: string;
12
+ onProgress?: (progress: DownloadItem) => void;
13
+ };
14
+
15
+ export async function downloadRequest(request: Request, { fileName, controller, onProgress }: DownloadParameters) {
16
+ function reportProgress(progress: DownloadItem) {
17
+ if (typeof onProgress === "function") {
18
+ onProgress({ ...progress });
19
+ }
20
+ }
21
+
22
+ let bytesReceived = 0;
23
+
24
+ reportProgress({ status: "requested" });
25
+
26
+ try {
27
+ let response = await fetch(request);
28
+
29
+ if (!response.ok || !response.body) {
30
+ throw new Error(response.status + " " + response.statusText);
31
+ }
32
+
33
+ try {
34
+ const reader = response.body.getReader();
35
+ const stream = new ReadableStream({
36
+ start: function (controller) {
37
+ function pump(): Promise<void> {
38
+ return reader.read().then(function (read) {
39
+ if (read.done) {
40
+ controller.close();
41
+ return;
42
+ }
43
+
44
+ bytesReceived += read.value.length;
45
+ reportProgress({ bytesReceived, status: "downloading" });
46
+ controller.enqueue(read.value);
47
+
48
+ return pump();
49
+ });
50
+ }
51
+
52
+ return pump();
53
+ },
54
+ });
55
+
56
+ response = new Response(stream);
57
+ } catch (error) {
58
+ // eslint-disable-next-line no-console
59
+ console.warn("Failed to replace response stream to log download progress.");
60
+ }
61
+
62
+ const blob = await response.blob();
63
+
64
+ reportProgress({ bytesReceived, status: "downloaded" });
65
+
66
+ await downloadBlob(blob, fileName);
67
+
68
+ reportProgress({ bytesReceived, status: "completed" });
69
+ } catch (err) {
70
+ if (controller && controller.signal && controller.signal.aborted) {
71
+ reportProgress({ status: "cancelled" });
72
+ } else {
73
+ reportProgress({ error: (err as Error).message, status: "error" });
74
+ }
75
+ }
76
+ }
@@ -0,0 +1,110 @@
1
+ import { DataObject, DataObjectOptions } from "@olenbetong/appframe-data";
2
+ import { RetrieveParameters } from "@olenbetong/appframe-data";
3
+
4
+ import { downloadRequest } from "./downloadRequest.js";
5
+
6
+ export type DataExportColumn = {
7
+ name: string;
8
+ caption?: string;
9
+ cellFormat?: string;
10
+ format?: string;
11
+ };
12
+
13
+ export type DataExportOptions = RetrieveParameters<any> & { columns: DataExportColumn[] };
14
+ export type DataObjectReportParams = {
15
+ articleId: string;
16
+ controller?: AbortController;
17
+ dataObject: string;
18
+ format: "pdf" | "xlsx" | "csv" | "xls";
19
+ fileName?: string;
20
+ options: DataExportOptions;
21
+ id?: string;
22
+ title?: string;
23
+ subTitle?: string;
24
+ /**
25
+ * Whether the title should be shown in the exported file (default true)
26
+ */
27
+ includeTitle?: boolean;
28
+ /**
29
+ * Whether the filter should be shown in the exported file (default true)
30
+ */
31
+ includefilter?: boolean;
32
+ /**
33
+ * Whether we should try to convert from camelCase to human readable names (default true)
34
+ */
35
+ formatHeaders?: boolean;
36
+ /**
37
+ * Whether the header should be frozen on top of the excel file.
38
+ */
39
+ freezeHeader?: boolean;
40
+ };
41
+
42
+ /**
43
+ * Get columns and retrieve parameters from a data object to be used to create a report.
44
+ * @param dataObject Data object to get parameters and columns from
45
+ * @param specifiedOptions Options to override the data object options
46
+ * @returns
47
+ */
48
+ export function exportOptionsFromDataObject(
49
+ dataObject: DataObject<any>,
50
+ specifiedOptions: Partial<DataObjectOptions<any>>
51
+ ) {
52
+ return {
53
+ columns: dataObject.getFields().map((field) => ({ name: field.name })),
54
+ distinctRows: dataObject.getParameter("distinctRows"),
55
+ filterObject: dataObject.getParameter("filterObject"),
56
+ filterString: dataObject.getParameter("filterString"),
57
+ maxRecords: dataObject.getParameter("maxRecords"),
58
+ sortOrder: dataObject.getParameter("sortOrder"),
59
+ whereClause: dataObject.getParameter("whereClause"),
60
+ whereObject: dataObject.getParameter("whereObject"),
61
+ ...specifiedOptions,
62
+ };
63
+ }
64
+
65
+ const defaultOptions: DataExportOptions = {
66
+ columns: [],
67
+ distinctRows: false,
68
+ filterObject: null,
69
+ filterString: "",
70
+ maxRecords: -1,
71
+ sortOrder: [],
72
+ whereClause: "",
73
+ whereObject: null,
74
+ };
75
+
76
+ /**
77
+ * Get a request object to download a report from a data object.
78
+ * @param params Parameters to use to create a report
79
+ * @returns Request object for the report
80
+ */
81
+ export function getDataObjectReportRequest(params: DataObjectReportParams) {
82
+ const { articleId, dataObject, format, options } = params;
83
+ const reportOptions = options ? Object.assign({}, defaultOptions, options) : defaultOptions;
84
+ const body = "data=" + encodeURIComponent(JSON.stringify(reportOptions));
85
+ const requestUrl = `/api/data-object/export/${articleId}/${dataObject}/${format}`;
86
+
87
+ const controller = params.controller ?? new AbortController();
88
+
89
+ return {
90
+ controller,
91
+ request: new Request(requestUrl, {
92
+ body,
93
+ credentials: "same-origin",
94
+ headers: {
95
+ "Content-Type": "application/x-www-form-urlencoded",
96
+ },
97
+ method: "POST",
98
+ signal: controller.signal,
99
+ }),
100
+ };
101
+ }
102
+
103
+ export function openDataObjectReport(params: DataObjectReportParams) {
104
+ const { dataObject, fileName, format } = params;
105
+ const { request } = getDataObjectReportRequest(params);
106
+
107
+ return downloadRequest(request, {
108
+ fileName: `${fileName || dataObject}.${format}`,
109
+ });
110
+ }
@@ -0,0 +1,80 @@
1
+ import type { DataObject, Procedure } from "@olenbetong/appframe-data";
2
+
3
+ function getFormValue(value: string, type: string): string | number | boolean | Date | BigInt | null | undefined {
4
+ if (value === "") {
5
+ return null;
6
+ } else if (type === "number") {
7
+ let numValue = Number(value);
8
+
9
+ return isNaN(numValue) ? undefined : numValue;
10
+ } else if (["date", "datetime", "time"].includes(type)) {
11
+ let dateValue = new Date(value);
12
+
13
+ return isNaN(dateValue.getTime()) ? undefined : dateValue;
14
+ } else if (type === "boolean") {
15
+ return value.toLowerCase() === "true";
16
+ } else if (type === "bigint") {
17
+ return BigInt(value);
18
+ }
19
+
20
+ return value;
21
+ }
22
+
23
+ /**
24
+ * Creates an object from form data. Uses the type map passed to convert the form values to the correct type.
25
+ * @param form HTML form element to get form data from.
26
+ * @param typeMap A mapping from field name to field type
27
+ * @returns
28
+ */
29
+ export function formDataToObject<T = any>(form: HTMLFormElement, typeMap: Record<string, string> = {}): Partial<T> {
30
+ let formData = new FormData(form);
31
+ let result: any = {};
32
+
33
+ for (let entry of formData.entries()) {
34
+ let field = entry[0];
35
+ let type = typeMap?.[field] ?? "string";
36
+ let value = getFormValue(entry[1] as string, type);
37
+
38
+ if (result.hasOwnProperty(field)) {
39
+ if (Array.isArray(result[field])) {
40
+ result[field].push(value);
41
+ } else {
42
+ result[field] = [result[field], value];
43
+ }
44
+ } else {
45
+ result[field] = value;
46
+ }
47
+ }
48
+
49
+ return result as T;
50
+ }
51
+
52
+ /**
53
+ * Creates a partial data object record from form data. The name property is mapped
54
+ * to field name.
55
+ *
56
+ * @param form HTML form element to get FormData from
57
+ * @param dataObject Data object for which to create a record from the form data
58
+ * @returns {Partial<T>} Partial data object record
59
+ */
60
+ export function formDataToDataObjectRecord<T>(form: HTMLFormElement, dataObject: DataObject<T>): Partial<T> {
61
+ let fields = dataObject.getFields();
62
+ let types = Object.fromEntries(fields.map((field) => [field.name, field.type])) as Record<string, string>;
63
+
64
+ return formDataToObject<T>(form, types);
65
+ }
66
+
67
+ /**
68
+ * Creates procedure parameters from form data. The name property is mapped
69
+ * to parameter names.
70
+ *
71
+ * @param form HTML Form element to get FormData from
72
+ * @param procedure Appframe Procedure to create parameters for
73
+ * @returns
74
+ */
75
+ export function formDataToProcedureParameters<T>(form: HTMLFormElement, procedure: Procedure<T, any>): T {
76
+ let parameters = procedure.getParameters();
77
+ let types = Object.fromEntries(parameters.map((param) => [param.name, param.type])) as Record<string, string>;
78
+
79
+ return formDataToObject(form, types) as T;
80
+ }
package/src/index.ts CHANGED
@@ -1,8 +1,12 @@
1
1
  export { default as mitt } from "mitt";
2
+ export { downloadBlob } from "./downloadBlob.js";
3
+ export * from "./downloadRequest.js";
4
+ export * from "./exportDataObject.js";
2
5
  export { default as expose } from "./expose.js";
3
6
  export { getLocalizedString } from "./getLocalizedString.js";
4
7
  export { default as localStorage } from "./localStorage.js";
5
8
  export { default as SessionHandler } from "./SessionHandler.js";
6
9
  export { default as UserSession } from "./UserSession.js";
10
+ export { formDataToDataObjectRecord, formDataToObject, formDataToProcedureParameters } from "./formDataToObject.js";
7
11
  export * from "./settings.js";
8
12
  export const version = __VERSION__;