@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.
@@ -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;
@@ -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-DzRwzM6G.js").then((o) => o.j), { default: w } = await import("./jspdf.plugin.autotable-DFaknRns.js"), c = new x({ orientation: "landscape" });
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-BI2MxWot.js";
2
- import { _ as Xa } from "./jspdf.es.min-DzRwzM6G.js";
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-BI2MxWot.js";
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-D-PR3JkH.js")).catch(function(k) {
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@isi-ui7/bos7-shared",
3
- "version": "0.3.7",
3
+ "version": "0.3.9",
4
4
  "description": "Shared auth7 and layout primitives for bos7 applications.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -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
- width={width}
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>