@isi-ui7/bos7-shared 0.3.7 → 0.3.9
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/dist/detail-modal-field.d.ts +47 -0
- package/dist/form-types.d.ts +89 -1
- package/dist/i18n.d.ts +9 -0
- package/dist/{index-BI2MxWot.js → index-DqWPV29B.js} +10 -2
- package/dist/{index.es-D-PR3JkH.js → index.es-9cvhYbjn.js} +2 -2
- package/dist/index.js +1 -1
- package/dist/{jspdf.es.min-DzRwzM6G.js → jspdf.es.min-CCBzPHw4.js} +1 -1
- package/package.json +1 -1
- package/src/crud-components.tsx +6 -1
- package/src/detail-modal-field.test.tsx +530 -0
- package/src/detail-modal-field.tsx +342 -0
- package/src/form-renderer.tsx +83 -0
- package/src/form-types.ts +94 -1
- package/src/i18n.tsx +19 -0
- package/src/wizard-stepper.tsx +1 -1
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { DetailModalConfig, FormMode, FormSection } from './form-types';
|
|
3
|
+
import { BosSharedLabels } from './i18n';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Tabel ringkas + modal form per baris — badan tipe field `detail-modal`.
|
|
7
|
+
*
|
|
8
|
+
* Semua gaya di sini INLINE (atau memakai kelas `ui7-form-*` yang sudah ikut
|
|
9
|
+
* `corporate-themes/dist/globals.css`), mengikuti `wizard-stepper.tsx`.
|
|
10
|
+
* Sengaja tidak ada berkas `.css` bersebelahan: `packages/bos7-shared` tidak
|
|
11
|
+
* punya `sideEffects`, tidak punya titik impor CSS, dan `form-contract.css`
|
|
12
|
+
* membuktikan jalur itu berakhir sebagai berkas yang tidak pernah dimuat siapa
|
|
13
|
+
* pun — komponennya tetap jalan, hanya tampilannya rusak, tanpa satu pun galat.
|
|
14
|
+
*/
|
|
15
|
+
export type DetailModalFieldProps = {
|
|
16
|
+
/** Dipakai untuk id/aria yang unik antar field dalam satu form. */
|
|
17
|
+
fieldKey: string;
|
|
18
|
+
label: ReactNode;
|
|
19
|
+
required?: boolean;
|
|
20
|
+
helperText?: ReactNode;
|
|
21
|
+
config: DetailModalConfig;
|
|
22
|
+
rows: Record<string, unknown>[];
|
|
23
|
+
mode: FormMode;
|
|
24
|
+
/** View mode atau field readonly: tabel tetap tampil, aksinya hilang. */
|
|
25
|
+
readOnly: boolean;
|
|
26
|
+
disabled: boolean;
|
|
27
|
+
labels: BosSharedLabels;
|
|
28
|
+
/** Pesan galat tingkat field dari validator form induk. */
|
|
29
|
+
invalidText?: string;
|
|
30
|
+
onChange: (rows: Record<string, unknown>[]) => void;
|
|
31
|
+
/**
|
|
32
|
+
* Perender form untuk isi modal.
|
|
33
|
+
*
|
|
34
|
+
* Disuntikkan dari `form-renderer.tsx` alih-alih di-import langsung supaya
|
|
35
|
+
* tidak ada lingkaran impor antara kedua modul — `form-renderer` sudah
|
|
36
|
+
* meng-import berkas ini.
|
|
37
|
+
*/
|
|
38
|
+
renderForm: (props: {
|
|
39
|
+
value: Record<string, unknown>;
|
|
40
|
+
sections: FormSection<Record<string, unknown>>[];
|
|
41
|
+
mode: FormMode;
|
|
42
|
+
errors: Partial<Record<string, string>>;
|
|
43
|
+
disabled: boolean;
|
|
44
|
+
onChange: (patch: Partial<Record<string, unknown>>) => void;
|
|
45
|
+
}) => ReactNode;
|
|
46
|
+
};
|
|
47
|
+
export declare function DetailModalField({ fieldKey, label, required, helperText, config, rows, mode, readOnly, disabled, labels, invalidText, onChange, renderForm, }: DetailModalFieldProps): import("react/jsx-runtime").JSX.Element;
|
package/dist/form-types.d.ts
CHANGED
|
@@ -19,7 +19,7 @@ export type FieldValidation<TData extends Record<string, unknown>> = {
|
|
|
19
19
|
/** Array of validators — compose Rules.* or custom functions. Runs after built-in rules. */
|
|
20
20
|
validators?: FieldValidatorFn<TData>[];
|
|
21
21
|
};
|
|
22
|
-
export type FormFieldType = "text" | "textarea" | "number" | "date" | "datetime" | "select" | "lookup" | "checkbox" | "toggle" | "detail-rows";
|
|
22
|
+
export type FormFieldType = "text" | "textarea" | "number" | "date" | "datetime" | "select" | "lookup" | "checkbox" | "toggle" | "detail-rows" | "detail-modal";
|
|
23
23
|
/**
|
|
24
24
|
* Select option. `value` may be a number for numeric-coded columns (e.g.
|
|
25
25
|
* kolektibilitas 1–5); the renderer compares via `String(value)` and the
|
|
@@ -106,6 +106,93 @@ export type DetailRowsConfig = {
|
|
|
106
106
|
*/
|
|
107
107
|
lockExistingRows?: boolean;
|
|
108
108
|
};
|
|
109
|
+
/**
|
|
110
|
+
* Satu kolom pada tabel RINGKAS `detail-modal`.
|
|
111
|
+
*
|
|
112
|
+
* Sengaja BUKAN `EditableColumnDef`: kolom di sini hanya menampilkan, tidak
|
|
113
|
+
* pernah menyunting — penyuntingan seluruhnya terjadi di dalam modal. Memakai
|
|
114
|
+
* `EditableColumnDef` akan menuntut `columnType` untuk setiap kolom padahal
|
|
115
|
+
* tipe itu hanya menentukan editor sel yang tidak akan pernah dipakai.
|
|
116
|
+
*/
|
|
117
|
+
export type DetailModalColumn = {
|
|
118
|
+
/** Nama properti pada baris. */
|
|
119
|
+
field: string;
|
|
120
|
+
header: ReactNode;
|
|
121
|
+
/** Lebar kolom dalam piksel. Tanpa ini kolom ikut lebar isi. */
|
|
122
|
+
width?: number;
|
|
123
|
+
/** `end` untuk angka. Default `start`. */
|
|
124
|
+
align?: "start" | "end";
|
|
125
|
+
/**
|
|
126
|
+
* Ubah nilai mentah jadi tampilan. Tanpa ini nilai kosong tampil sebagai
|
|
127
|
+
* em-dash (`labels.emptyValue`) dan sisanya lewat `String(value)`.
|
|
128
|
+
*
|
|
129
|
+
* ⚠️ Kolom ringkas menampilkan nilai SIMPAN, bukan label. Untuk field
|
|
130
|
+
* `select`, `String(value)` menghasilkan kodenya ("M"), bukan "Laki-laki" —
|
|
131
|
+
* pakai `format` bila yang harus terbaca operator adalah labelnya.
|
|
132
|
+
*/
|
|
133
|
+
format?: (value: unknown, row: Record<string, unknown>) => ReactNode;
|
|
134
|
+
};
|
|
135
|
+
/**
|
|
136
|
+
* Detail-modal config — tabel RINGKAS + tombol Tambah yang membuka modal
|
|
137
|
+
* berisi form penuh untuk SATU baris.
|
|
138
|
+
*
|
|
139
|
+
* Bedanya dengan `detail-rows`: `detail-rows` menyunting DI DALAM sel, jadi
|
|
140
|
+
* setiap field baris harus muat sebagai satu kolom. Begitu satu baris punya
|
|
141
|
+
* belasan field (Beneficial Owner: ~20 field + dua blok alamat), barisnya jadi
|
|
142
|
+
* selebar beberapa layar dan tidak ada tempat untuk alur per-baris seperti cek
|
|
143
|
+
* identitas. `detail-modal` memindahkan seluruh form baris ke modal, dan
|
|
144
|
+
* menyisakan hanya beberapa kolom penanda di tabel.
|
|
145
|
+
*
|
|
146
|
+
* Nilai field di `key` harus berupa array objek biasa; renderer membaca dan
|
|
147
|
+
* menulis array itu secara utuh lewat `onChange({ [key]: newRows })` — sama
|
|
148
|
+
* seperti `detail-rows`.
|
|
149
|
+
*
|
|
150
|
+
* Isi modal dirender oleh `SchemaFormRenderer` yang sama dengan form induknya,
|
|
151
|
+
* jadi SEMUA tipe field yang sudah ada (lookup, date, numeric, toggle, bahkan
|
|
152
|
+
* `FormSection.render`) langsung bisa dipakai di dalam baris tanpa tambahan
|
|
153
|
+
* apa pun di sini.
|
|
154
|
+
*/
|
|
155
|
+
export type DetailModalConfig = {
|
|
156
|
+
/**
|
|
157
|
+
* Form untuk SATU baris. Section-nya sama persis dengan section form biasa,
|
|
158
|
+
* dan `TData`-nya adalah barisnya — bukan form induk.
|
|
159
|
+
*/
|
|
160
|
+
sections: FormSection<Record<string, unknown>>[];
|
|
161
|
+
/** Kolom yang tampil di tabel ringkas. Boleh sebagian kecil saja dari field baris. */
|
|
162
|
+
columns: DetailModalColumn[];
|
|
163
|
+
newRowFactory: () => Record<string, unknown>;
|
|
164
|
+
/**
|
|
165
|
+
* Aturan lintas-field untuk satu baris, dijalankan SETELAH validasi per-field
|
|
166
|
+
* milik `sections`. Kuncinya nama field; hasilnya menimpa error per-field.
|
|
167
|
+
*
|
|
168
|
+
* `index` adalah posisi baris — untuk baris baru, posisi tempat ia akan
|
|
169
|
+
* mendarat (`rows.length`).
|
|
170
|
+
*/
|
|
171
|
+
validateRow?: (row: Record<string, unknown>, index: number) => Record<string, string>;
|
|
172
|
+
/** Batas jumlah baris. Tombol Tambah nonaktif setelah tercapai. */
|
|
173
|
+
maxRows?: number;
|
|
174
|
+
/** Teks tombol tambah, mis. "Tambah Ahli Waris". Default: "Tambah". */
|
|
175
|
+
addLabel?: ReactNode;
|
|
176
|
+
/** Judul modal saat menambah. Default: "Tambah Data". */
|
|
177
|
+
addModalTitle?: ReactNode;
|
|
178
|
+
/** Judul modal saat mengubah. Default: "Ubah Data". */
|
|
179
|
+
editModalTitle?: ReactNode;
|
|
180
|
+
/** Teks saat tabel kosong. Default: "Belum ada data." */
|
|
181
|
+
emptyText?: ReactNode;
|
|
182
|
+
/** Lebar modal Carbon. Default `lg` — form baris biasanya padat. */
|
|
183
|
+
modalSize?: "xs" | "sm" | "md" | "lg";
|
|
184
|
+
/** Izinkan membuka baris yang sudah ada untuk diubah. Default `true`. */
|
|
185
|
+
allowEdit?: boolean;
|
|
186
|
+
/** Izinkan menghapus baris. Default `true`. */
|
|
187
|
+
allowDelete?: boolean;
|
|
188
|
+
/**
|
|
189
|
+
* Kunci React per baris. Default memakai indeks.
|
|
190
|
+
*
|
|
191
|
+
* Berguna bila baris punya identitas sendiri (mis. `person_id`) supaya state
|
|
192
|
+
* modal tidak nyangkut ke baris lain setelah penghapusan di tengah.
|
|
193
|
+
*/
|
|
194
|
+
rowKey?: (row: Record<string, unknown>, index: number) => string;
|
|
195
|
+
};
|
|
109
196
|
export type FormField<TData extends Record<string, unknown>> = {
|
|
110
197
|
key: keyof TData;
|
|
111
198
|
label: ReactNode;
|
|
@@ -138,6 +225,7 @@ export type FormField<TData extends Record<string, unknown>> = {
|
|
|
138
225
|
numeric?: FormFieldNumericConfig<TData>;
|
|
139
226
|
toggle?: FormFieldToggleConfig;
|
|
140
227
|
detailRows?: DetailRowsConfig;
|
|
228
|
+
detailModal?: DetailModalConfig;
|
|
141
229
|
format?: (value: TData[keyof TData], data: TData) => ReactNode;
|
|
142
230
|
validation?: FieldValidation<TData>;
|
|
143
231
|
};
|
package/dist/i18n.d.ts
CHANGED
|
@@ -34,5 +34,14 @@ export type BosSharedLabels = {
|
|
|
34
34
|
deleteTitle: string;
|
|
35
35
|
deleteDescription: string;
|
|
36
36
|
deleteConfirm: string;
|
|
37
|
+
/** Tipe field `detail-modal` — tabel ringkas + modal form per baris. */
|
|
38
|
+
detailAdd: string;
|
|
39
|
+
detailEdit: string;
|
|
40
|
+
detailDelete: string;
|
|
41
|
+
detailEmpty: string;
|
|
42
|
+
detailNo: string;
|
|
43
|
+
detailActions: string;
|
|
44
|
+
detailAddTitle: string;
|
|
45
|
+
detailEditTitle: string;
|
|
37
46
|
};
|
|
38
47
|
export declare function useBosSharedI18n(): BosSharedLabels;
|
|
@@ -27391,7 +27391,7 @@ function ba(M, L) {
|
|
|
27391
27391
|
return String(pr(M, L));
|
|
27392
27392
|
}
|
|
27393
27393
|
async function Io(M, L, r) {
|
|
27394
|
-
const { default: x } = await import("./jspdf.es.min-
|
|
27394
|
+
const { default: x } = await import("./jspdf.es.min-CCBzPHw4.js").then((o) => o.j), { default: w } = await import("./jspdf.plugin.autotable-DFaknRns.js"), c = new x({ orientation: "landscape" });
|
|
27395
27395
|
c.setFontSize(14), c.text(r, 14, 15), c.setFontSize(10), w(c, {
|
|
27396
27396
|
head: [L.map((o) => o.title)],
|
|
27397
27397
|
body: M.map((o) => L.map((a) => ba(o[a.field], a.displayFormat))),
|
|
@@ -28654,7 +28654,15 @@ function tl() {
|
|
|
28654
28654
|
submitEdit: M("bos7.submitEdit", "Update"),
|
|
28655
28655
|
deleteTitle: M("bos7.deleteTitle", "Konfirmasi Hapus"),
|
|
28656
28656
|
deleteDescription: M("bos7.deleteDescription", "Data berikut akan dihapus permanen."),
|
|
28657
|
-
deleteConfirm: M("bos7.deleteConfirm", "Hapus")
|
|
28657
|
+
deleteConfirm: M("bos7.deleteConfirm", "Hapus"),
|
|
28658
|
+
detailAdd: M("bos7.detailAdd", "Tambah"),
|
|
28659
|
+
detailEdit: M("bos7.detailEdit", "Ubah"),
|
|
28660
|
+
detailDelete: M("bos7.detailDelete", "Hapus"),
|
|
28661
|
+
detailEmpty: M("bos7.detailEmpty", "Belum ada data."),
|
|
28662
|
+
detailNo: M("bos7.detailNo", "No"),
|
|
28663
|
+
detailActions: M("bos7.detailActions", "Aksi"),
|
|
28664
|
+
detailAddTitle: M("bos7.detailAddTitle", "Tambah Data"),
|
|
28665
|
+
detailEditTitle: M("bos7.detailEditTitle", "Ubah Data")
|
|
28658
28666
|
};
|
|
28659
28667
|
}
|
|
28660
28668
|
function Ri(M, L) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { c as Ke, g as Do } from "./index-
|
|
2
|
-
import { _ as Xa } from "./jspdf.es.min-
|
|
1
|
+
import { c as Ke, g as Do } from "./index-DqWPV29B.js";
|
|
2
|
+
import { _ as Xa } from "./jspdf.es.min-CCBzPHw4.js";
|
|
3
3
|
var vt = function(a) {
|
|
4
4
|
return a && a.Math === Math && a;
|
|
5
5
|
}, _ = (
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as s, a as o, b as t, D as r, N as i, O as n, d as l, P as c, e as d, f as u, h as S, U as h, i as A, j as T, k, l as C, m as _, n as O, o as U, p as g, q as N, r as P, s as m, t as E, u as F, v as p, w as f, x as I, y as R, z as x, B as v, C as y, E as L, F as b, G as B, H as D, I as M, J as H, K as V, L as W, M as q, Q as K, R as w, S as z, T as G, V as Q, W as X, X as Y, Y as j, Z as J, _ as Z, $, a0 as aa, a1 as ea, a2 as sa, a3 as oa, a4 as ta, a5 as ra, a6 as ia, a7 as na, a8 as la, a9 as ca, aa as da, ab as ua, ac as Sa, ad as ha, ae as Aa, af as Ta, ag as ka, ah as Ca, ai as _a } from "./index-
|
|
1
|
+
import { A as s, a as o, b as t, D as r, N as i, O as n, d as l, P as c, e as d, f as u, h as S, U as h, i as A, j as T, k, l as C, m as _, n as O, o as U, p as g, q as N, r as P, s as m, t as E, u as F, v as p, w as f, x as I, y as R, z as x, B as v, C as y, E as L, F as b, G as B, H as D, I as M, J as H, K as V, L as W, M as q, Q as K, R as w, S as z, T as G, V as Q, W as X, X as Y, Y as j, Z as J, _ as Z, $, a0 as aa, a1 as ea, a2 as sa, a3 as oa, a4 as ta, a5 as ra, a6 as ia, a7 as na, a8 as la, a9 as ca, aa as da, ab as ua, ac as Sa, ad as ha, ae as Aa, af as Ta, ag as ka, ah as Ca, ai as _a } from "./index-DqWPV29B.js";
|
|
2
2
|
export {
|
|
3
3
|
s as AUTH7_SCOPES,
|
|
4
4
|
o as AppShellLayout,
|
|
@@ -9216,7 +9216,7 @@ function(n) {
|
|
|
9216
9216
|
var d = h.getContext("2d");
|
|
9217
9217
|
d.fillStyle = "#fff", d.fillRect(0, 0, h.width, h.height);
|
|
9218
9218
|
var m = { ignoreMouse: !0, ignoreAnimation: !0, ignoreDimensions: !0 }, _ = this;
|
|
9219
|
-
return (Kt.canvg ? Promise.resolve(Kt.canvg) : import("./index.es-
|
|
9219
|
+
return (Kt.canvg ? Promise.resolve(Kt.canvg) : import("./index.es-9cvhYbjn.js")).catch(function(k) {
|
|
9220
9220
|
return Promise.reject(new Error("Could not load canvg: " + k));
|
|
9221
9221
|
}).then(function(k) {
|
|
9222
9222
|
return k.default ? k.default : k;
|
package/package.json
CHANGED
package/src/crud-components.tsx
CHANGED
|
@@ -588,7 +588,12 @@ export function CrudSchemaPage<TData extends Record<string, unknown>>({
|
|
|
588
588
|
disabled={saving}
|
|
589
589
|
onChange={setForm}
|
|
590
590
|
density={density}
|
|
591
|
-
|
|
591
|
+
// Pada wizard, form MENGISI kolom kirinya. Default global
|
|
592
|
+
// "two-thirds" membatasi form di dalam kolom yang SUDAH
|
|
593
|
+
// dipersempit stepper — hasilnya sepertiga ruang kosong
|
|
594
|
+
// menganga di antara keduanya. Penulis skema tetap bisa
|
|
595
|
+
// menyempitkannya dgn menyetel schema.width secara eksplisit.
|
|
596
|
+
width={width ?? "full"}
|
|
592
597
|
/>
|
|
593
598
|
) : null}
|
|
594
599
|
</div>
|