@wipco/sdui 0.19.2 → 0.21.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.
@@ -1,6 +1,24 @@
1
1
  import { S as SduiBoxProps } from './layout-B1QrIBm6.js';
2
2
  import React__default from 'react';
3
3
 
4
+ interface TimePickerProps {
5
+ /** 선택된 시각 (HH:MM, 24시간제). 주어지면 controlled로 동작. */
6
+ value?: string;
7
+ onChange?: (value: string) => void;
8
+ /** 고를 수 있는 최소 시각 (HH:MM, 포함). */
9
+ min?: string;
10
+ /** 고를 수 있는 최대 시각 (HH:MM, 포함). */
11
+ max?: string;
12
+ /** 목록 눈금 간격(분). 기본 30 */
13
+ step?: number;
14
+ placeholder?: string;
15
+ invalid?: boolean;
16
+ disabled?: boolean;
17
+ size?: 'sm' | 'md' | 'lg';
18
+ }
19
+ /** 바깥에서 온 `style`·`className`을 뿌리에 얹어 내보낸다. 까닭은 {@link ./boxed.tsx}에 있다. */
20
+ declare const TimePicker: React__default.FC<TimePickerProps & SduiBoxProps>;
21
+
4
22
  interface DatePickerProps {
5
23
  /** 선택된 날짜 (YYYY-MM-DD, `withTime`이면 YYYY-MM-DDTHH:mm). 주어지면 controlled로 동작. */
6
24
  value?: string;
@@ -45,4 +63,4 @@ declare const DatePicker: React__default.FC<DatePickerProps & SduiBoxProps>;
45
63
  /** 바깥에서 온 `style`·`className`을 뿌리에 얹어 내보낸다. 까닭은 {@link ./boxed.tsx}에 있다. */
46
64
  declare const DateRangePicker: React__default.FC<DateRangePickerProps & SduiBoxProps>;
47
65
 
48
- export { type DatePickerProps as D, type DateRangePickerProps as a, DatePicker as b, DateRangePicker as c, type DateRangeValue as d };
66
+ export { type DatePickerProps as D, type TimePickerProps as T, type DateRangePickerProps as a, DatePicker as b, DateRangePicker as c, type DateRangeValue as d, TimePicker as e };
@@ -1,5 +1,5 @@
1
- import { resolveTreeNodes, TreeView } from './chunk-TUR4EBKX.js';
2
- import { OverlayLayer } from './chunk-IY7SMV6V.js';
1
+ import { resolveTreeNodes, TreeView } from './chunk-IDUN2ESE.js';
2
+ import { OverlayLayer } from './chunk-XKLUCUVA.js';
3
3
  import { Field, Dialog, Icon, Button, Input } from './chunk-WIDDHR7L.js';
4
4
  import { boxed } from './chunk-SAN4IX6N.js';
5
5
  import { useState, useMemo, useEffect } from 'react';
@@ -0,0 +1,291 @@
1
+ import { useDismiss, usePopoverAnchor, usePortalTo, HoistedStyle, AnchoredPopoverStyles, anchorStyle, focusStaysInside } from './chunk-XKLUCUVA.js';
2
+ import { Icon } from './chunk-WIDDHR7L.js';
3
+ import { boxed } from './chunk-SAN4IX6N.js';
4
+ import { createContext, useState, useRef, useEffect, useMemo, useCallback, useContext } from 'react';
5
+ import { jsxs, jsx } from 'react/jsx-runtime';
6
+
7
+ // src/env.ts
8
+ var DEV = (() => {
9
+ try {
10
+ const meta = import.meta;
11
+ return Boolean(meta.env?.DEV);
12
+ } catch {
13
+ return false;
14
+ }
15
+ })();
16
+
17
+ // src/adapter.ts
18
+ function isSafeNavigationTarget(to) {
19
+ if (typeof to !== "string") return false;
20
+ const value = to.replace(/[\u0000-\u0020]/g, "");
21
+ if (value === "") return false;
22
+ if (value.startsWith("//")) return false;
23
+ if (value.startsWith("/") || value.startsWith("?") || value.startsWith("#")) return true;
24
+ return /^https?:\/\//i.test(value);
25
+ }
26
+ async function defaultFetch(path, init) {
27
+ const response = await fetch(path, {
28
+ method: init.method,
29
+ headers: { "content-type": "application/json", accept: "application/json" },
30
+ credentials: "include",
31
+ body: init.body === void 0 ? void 0 : JSON.stringify(init.body),
32
+ signal: init.signal
33
+ });
34
+ const text = await response.text();
35
+ let json = null;
36
+ if (text) {
37
+ try {
38
+ json = JSON.parse(text);
39
+ } catch {
40
+ json = null;
41
+ }
42
+ }
43
+ if (!response.ok) {
44
+ const message = json && typeof json === "object" && typeof json.message === "string" ? json.message : `\uC694\uCCAD\uC774 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4 (${response.status})`;
45
+ throw new Error(message);
46
+ }
47
+ return json;
48
+ }
49
+ var defaultAdapter = {
50
+ navigate: (to) => {
51
+ if (!isSafeNavigationTarget(to)) {
52
+ console.error(`[sdui] \uD5C8\uC6A9\uB418\uC9C0 \uC54A\uB294 navigate \uB300\uC0C1: ${String(to)}`);
53
+ return;
54
+ }
55
+ window.location.assign(to);
56
+ },
57
+ fetch: defaultFetch,
58
+ rawFetch: (path) => fetch(path, { credentials: "include" }),
59
+ // 항등함수. 계약을 문서화하고, 컴포넌트가 `useSduiAssetResolver()`를 조건 없이
60
+ // 부를 수 있게 하는 것이 전부다. **이 함수 참조 자체가 표식**이기도 하다 —
61
+ // `assetResolverOf`가 이것과 같은지 보고 "주입 없음"을 판정한다.
62
+ resolveAsset: (url) => url,
63
+ invalidate: (prefix) => {
64
+ if (DEV) console.warn(`[sdui] invalidate \uC561\uC158\uC774 \uC5B4\uB311\uD130 \uC5C6\uC774 \uD638\uCD9C\uB428 (prefix: ${prefix})`);
65
+ },
66
+ refresh: () => {
67
+ if (DEV) console.warn("[sdui] refresh \uC561\uC158\uC774 \uC5B4\uB311\uD130 \uC5C6\uC774 \uD638\uCD9C\uB428");
68
+ }
69
+ };
70
+ function resolveAdapter(adapter) {
71
+ const merged = { ...defaultAdapter };
72
+ if (adapter) {
73
+ for (const [key, value] of Object.entries(adapter)) {
74
+ if (value !== void 0) merged[key] = value;
75
+ }
76
+ }
77
+ return merged;
78
+ }
79
+ var SduiAdapterContext = createContext(null);
80
+ function useSduiAdapter() {
81
+ return useContext(SduiAdapterContext);
82
+ }
83
+ function assetResolverOf(adapter) {
84
+ const resolve = adapter?.resolveAsset;
85
+ if (!resolve || resolve === defaultAdapter.resolveAsset) return null;
86
+ return resolve;
87
+ }
88
+ function useSduiAssetResolver() {
89
+ const adapter = useContext(SduiAdapterContext);
90
+ return adapter?.resolveAsset ?? defaultAdapter.resolveAsset;
91
+ }
92
+ function useSduiSandbox() {
93
+ return useContext(SduiAdapterContext)?.sandbox === true;
94
+ }
95
+ function hostFetchOf(adapter) {
96
+ const hostFetch = adapter?.fetch;
97
+ if (!hostFetch || hostFetch === defaultAdapter.fetch) return null;
98
+ return hostFetch;
99
+ }
100
+ var HM_RE = /^\d{2}:\d{2}$/;
101
+ var LIST_HEIGHT = 260;
102
+ var MINUTES_PER_DAY = 24 * 60;
103
+ function maskHm(text) {
104
+ const digits = text.replace(/\D/g, "").slice(0, 4);
105
+ if (digits.length <= 2) return digits;
106
+ return `${digits.slice(0, 2)}:${digits.slice(2)}`;
107
+ }
108
+ function parseHm(value) {
109
+ if (typeof value !== "string" || !HM_RE.test(value)) return void 0;
110
+ const h = Number(value.slice(0, 2));
111
+ const m = Number(value.slice(3, 5));
112
+ if (h > 23 || m > 59) return void 0;
113
+ return h * 60 + m;
114
+ }
115
+ function formatHm(minutes) {
116
+ const h = Math.floor(minutes / 60) % 24;
117
+ const m = minutes % 60;
118
+ return `${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}`;
119
+ }
120
+ function buildOptions(step, min, max) {
121
+ const safeStep = Number.isFinite(step) && step >= 1 ? Math.min(Math.floor(step), 720) : 30;
122
+ const from = min ?? 0;
123
+ const to = max ?? MINUTES_PER_DAY - 1;
124
+ const out = [];
125
+ for (let m = Math.ceil(from / safeStep) * safeStep; m <= to; m += safeStep) out.push(formatHm(m));
126
+ return out;
127
+ }
128
+ function TimePickerImpl({
129
+ value,
130
+ onChange,
131
+ min,
132
+ max,
133
+ step = 30,
134
+ placeholder,
135
+ invalid = false,
136
+ disabled = false,
137
+ size = "md"
138
+ }) {
139
+ const [open, setOpen] = useState(false);
140
+ const [innerValue, setInnerValue] = useState("");
141
+ const hostRef = useRef(null);
142
+ const popRef = useRef(null);
143
+ const inputRef = useRef(null);
144
+ const isControlled = value !== void 0;
145
+ const currentValue = (isControlled ? value : innerValue) ?? "";
146
+ const [text, setText] = useState(currentValue);
147
+ useEffect(() => {
148
+ setText(currentValue);
149
+ }, [currentValue]);
150
+ const minMinutes = useMemo(() => parseHm(min), [min]);
151
+ const maxMinutes = useMemo(() => parseHm(max), [max]);
152
+ const options = useMemo(() => buildOptions(step, minMinutes, maxMinutes), [step, minMinutes, maxMinutes]);
153
+ const dismiss = useCallback((reason) => {
154
+ setOpen(false);
155
+ if (reason === "escape") inputRef.current?.focus();
156
+ }, []);
157
+ useDismiss(open, hostRef, popRef, dismiss);
158
+ const anchor = usePopoverAnchor(open, hostRef, popRef, LIST_HEIGHT);
159
+ const portal = usePortalTo();
160
+ const isInvalid = Boolean(invalid);
161
+ const commit = (next) => {
162
+ if (!isControlled) setInnerValue(next);
163
+ onChange?.(next);
164
+ };
165
+ const handleTextChange = (raw) => {
166
+ const masked = maskHm(raw);
167
+ setText(masked);
168
+ if (masked === "") {
169
+ commit("");
170
+ return;
171
+ }
172
+ const minutes = parseHm(masked);
173
+ if (minutes === void 0) return;
174
+ if (minMinutes !== void 0 && minutes < minMinutes) return;
175
+ if (maxMinutes !== void 0 && minutes > maxMinutes) return;
176
+ commit(masked);
177
+ };
178
+ const focusList = () => {
179
+ const list = popRef.current;
180
+ if (!list) return;
181
+ (list.querySelector('[aria-selected="true"]') ?? list.querySelector('[role="option"]'))?.focus();
182
+ };
183
+ const handleKeyDown = (event) => {
184
+ if (event.key === "ArrowDown") {
185
+ event.preventDefault();
186
+ if (open) focusList();
187
+ else setOpen(true);
188
+ return;
189
+ }
190
+ if (event.key === "Enter" && open) {
191
+ event.preventDefault();
192
+ setOpen(false);
193
+ }
194
+ };
195
+ const handleOptionKeyDown = (index) => (event) => {
196
+ const step2 = event.key === "ArrowDown" ? 1 : event.key === "ArrowUp" ? -1 : 0;
197
+ if (step2 !== 0) {
198
+ event.preventDefault();
199
+ const next = Math.min(Math.max(index + step2, 0), options.length - 1);
200
+ popRef.current?.querySelectorAll('[role="option"]')[next]?.focus();
201
+ return;
202
+ }
203
+ if (event.key === "Home" || event.key === "End") {
204
+ event.preventDefault();
205
+ const target = event.key === "Home" ? 0 : options.length - 1;
206
+ popRef.current?.querySelectorAll('[role="option"]')[target]?.focus();
207
+ }
208
+ };
209
+ const pick = (option) => {
210
+ setText(option);
211
+ commit(option);
212
+ setOpen(false);
213
+ inputRef.current?.focus();
214
+ };
215
+ const handleBlur = (event) => {
216
+ if (focusStaysInside(event.relatedTarget, hostRef, popRef)) return;
217
+ if (text !== currentValue) setText(currentValue);
218
+ setOpen(false);
219
+ };
220
+ const classes = ["wp-input", "wp-input--mono", "wp-tp-input"];
221
+ if (size !== "md") classes.push(`wp-input--${size}`);
222
+ if (isInvalid) classes.push("wp-input--invalid");
223
+ return /* @__PURE__ */ jsxs("div", { ref: hostRef, className: "wp-tp-host", onBlur: handleBlur, children: [
224
+ /* @__PURE__ */ jsx(HoistedStyle, { id: "wp-timepicker", css: TP_CSS }),
225
+ /* @__PURE__ */ jsx(AnchoredPopoverStyles, {}),
226
+ /* @__PURE__ */ jsxs("div", { className: "wp-inputwrap wp-inputwrap--lead", children: [
227
+ /* @__PURE__ */ jsx("span", { className: "wp-inputwrap__lead", children: /* @__PURE__ */ jsx(Icon, { name: "clock", size: size === "lg" ? 16 : 15 }) }),
228
+ /* @__PURE__ */ jsx(
229
+ "input",
230
+ {
231
+ ref: inputRef,
232
+ type: "text",
233
+ className: classes.join(" "),
234
+ inputMode: "numeric",
235
+ autoComplete: "off",
236
+ spellCheck: false,
237
+ maxLength: 5,
238
+ placeholder: placeholder ?? "HH:MM",
239
+ value: text,
240
+ disabled,
241
+ "aria-invalid": isInvalid || void 0,
242
+ "aria-haspopup": "listbox",
243
+ "aria-expanded": open,
244
+ onChange: (event) => handleTextChange(event.target.value),
245
+ onKeyDown: handleKeyDown,
246
+ onPointerDown: () => {
247
+ if (!disabled && !open) setOpen(true);
248
+ }
249
+ }
250
+ )
251
+ ] }),
252
+ portal(
253
+ anchor,
254
+ /* @__PURE__ */ jsx(
255
+ "div",
256
+ {
257
+ ref: popRef,
258
+ className: "wp-anchored-pop wp-tp-pop",
259
+ style: anchorStyle(anchor),
260
+ role: "listbox",
261
+ "aria-label": "\uC2DC\uAC01 \uC120\uD0DD",
262
+ children: options.map((option, index) => /* @__PURE__ */ jsx(
263
+ "button",
264
+ {
265
+ type: "button",
266
+ role: "option",
267
+ "aria-selected": option === currentValue,
268
+ className: option === currentValue ? "wp-tp-opt wp-tp-opt--on" : "wp-tp-opt",
269
+ onKeyDown: handleOptionKeyDown(index),
270
+ onClick: () => pick(option),
271
+ children: option
272
+ },
273
+ option
274
+ ))
275
+ }
276
+ )
277
+ )
278
+ ] });
279
+ }
280
+ var TP_CSS = `
281
+ .wp-tp-host{position:relative}
282
+ .wp-tp-input{padding-right:6px;min-width:calc(5ch + 36px)}
283
+ .wp-tp-pop{padding:var(--wp-space-1);max-height:260px;overflow-y:auto;overscroll-behavior:contain}
284
+ .wp-tp-opt{display:block;width:100%;padding:6px 10px;border:none;background:transparent;border-radius:var(--wp-radius-sm);font:inherit;font-family:var(--wp-font-mono);font-size:12.5px;color:var(--wp-text-default);text-align:left;cursor:pointer}
285
+ .wp-tp-opt:hover{background:var(--wp-surface-hover)}
286
+ .wp-tp-opt:focus-visible{outline:2px solid var(--wp-border-focus);outline-offset:-2px}
287
+ .wp-tp-opt--on{background:var(--wp-action-primary-bg);color:var(--wp-action-primary-fg);font-weight:500}
288
+ `;
289
+ var TimePicker = boxed(TimePickerImpl, "TimePicker");
290
+
291
+ export { DEV, SduiAdapterContext, TimePicker, assetResolverOf, defaultAdapter, hostFetchOf, isSafeNavigationTarget, resolveAdapter, useSduiAdapter, useSduiAssetResolver, useSduiSandbox };
@@ -1,4 +1,4 @@
1
- import { buildCascadingTree } from './chunk-GUFIEWW2.js';
1
+ import { buildCascadingTree } from './chunk-U7ZK7WYH.js';
2
2
  import { Input, IconButton, EmptyState, Icon, Badge } from './chunk-WIDDHR7L.js';
3
3
  import { boxed } from './chunk-SAN4IX6N.js';
4
4
  import React, { useMemo, useState, useRef, useEffect } from 'react';
@@ -0,0 +1,133 @@
1
+ import { boxed } from './chunk-SAN4IX6N.js';
2
+ import { useState } from 'react';
3
+ import { jsx, jsxs } from 'react/jsx-runtime';
4
+
5
+ var RECORD_SHEET_CSS = `
6
+ .erp-recsheet__scroll{overflow-x:auto;min-width:0}
7
+ .erp-recsheet{width:100%;min-width:44rem;border-collapse:collapse;table-layout:fixed;font-size:var(--wp-size-sm)}
8
+ .erp-recsheet td{height:var(--wp-row-h-dense);border:var(--wp-border-w) solid var(--wp-border-default);padding:var(--wp-space-1) var(--wp-space-2);color:var(--wp-text-default);vertical-align:middle;line-height:var(--wp-leading-snug);overflow-wrap:anywhere}
9
+ .erp-recsheet td.erp-recsheet__title{background:var(--wp-surface-inverse);color:var(--wp-text-inverse);text-align:center;font-size:var(--wp-size-lg);font-weight:var(--wp-weight-semibold);letter-spacing:var(--wp-tracking-heading);padding:var(--wp-space-3) var(--wp-space-2)}
10
+ .erp-recsheet td.erp-recsheet__label{background:var(--wp-surface-sunken);color:var(--wp-text-subtle);font-size:var(--wp-size-xs);font-weight:var(--wp-weight-semibold);text-align:center;white-space:nowrap}
11
+ .erp-recsheet td.erp-recsheet__section{background:var(--wp-surface-brand-tint);border-top:var(--wp-border-w-strong) solid var(--wp-border-brand);color:var(--wp-text-brand);font-size:var(--wp-size-xs);font-weight:var(--wp-weight-semibold);letter-spacing:var(--wp-tracking-heading)}
12
+ .erp-recsheet tr.erp-recsheet__gap td{border:0;padding:0;height:var(--wp-space-3)}
13
+ .erp-recsheet td.erp-recsheet__mono{font-family:var(--wp-font-mono);font-size:var(--wp-size-xs)}
14
+ .erp-recsheet td.erp-recsheet__photo{background:var(--wp-surface-sunken);padding:var(--wp-space-2);text-align:center}
15
+ .erp-recsheet__img{display:block;width:100%;height:11rem;object-fit:cover;border-radius:var(--wp-radius-sm)}
16
+ .erp-recsheet__noimg{display:flex;align-items:center;justify-content:center;width:100%;height:11rem;border:var(--wp-border-w) dashed var(--wp-border-default);border-radius:var(--wp-radius-sm);color:var(--wp-text-disabled);font-size:var(--wp-size-xs)}
17
+ .erp-recsheet__note{margin:var(--wp-space-2) 0 0;text-align:right;color:var(--wp-text-subtle);font-size:var(--wp-size-xs)}
18
+ @media print{
19
+ .erp-recsheet__scroll{overflow-x:visible}
20
+ .erp-recsheet{min-width:0}
21
+ .erp-recsheet tr{break-inside:avoid}
22
+ }
23
+ `;
24
+ function RecordSheetStyles() {
25
+ return /* @__PURE__ */ jsx("style", { href: "erp-recsheet", precedence: "medium", children: RECORD_SHEET_CSS });
26
+ }
27
+ function gridColumns(columns) {
28
+ if (typeof columns !== "number" || !Number.isFinite(columns)) return 24;
29
+ return Math.min(60, Math.max(1, Math.floor(columns)));
30
+ }
31
+ function cellClass(cell) {
32
+ const classes = [];
33
+ if (cell.kind === "label") classes.push("erp-recsheet__label");
34
+ else if (cell.kind === "section") classes.push("erp-recsheet__section");
35
+ else if (cell.kind === "photo") classes.push("erp-recsheet__photo");
36
+ if (cell.mono) classes.push("erp-recsheet__mono");
37
+ return classes.length > 0 ? classes.join(" ") : void 0;
38
+ }
39
+ function isBlank(value) {
40
+ return value === null || value === void 0 || value === "";
41
+ }
42
+ function pickValue(cell, item) {
43
+ if (!item || typeof item !== "object") return void 0;
44
+ const row = item;
45
+ if (cell.fields && cell.fields.length > 0) {
46
+ const parts = cell.fields.map((name) => isBlank(row[name]) ? "" : String(row[name]));
47
+ if (parts.every((part) => part === "")) return void 0;
48
+ return parts.join(cell.separator ?? " ~ ");
49
+ }
50
+ if (cell.field) return row[cell.field];
51
+ return void 0;
52
+ }
53
+ function PhotoCell({ src, alt }) {
54
+ const [failed, setFailed] = useState(false);
55
+ const label = alt ?? "\uC0AC\uC9C4";
56
+ if (!src || failed) return /* @__PURE__ */ jsx("span", { className: "erp-recsheet__noimg", children: label });
57
+ return /* @__PURE__ */ jsx("img", { className: "erp-recsheet__img", src, alt: label, onError: () => setFailed(true) });
58
+ }
59
+ function cellContent(cell, item, repeated) {
60
+ if (cell.kind === "photo") return /* @__PURE__ */ jsx(PhotoCell, { src: cell.src, alt: cell.alt });
61
+ if (!repeated) {
62
+ if (!isBlank(cell.text)) return cell.text;
63
+ if (cell.kind === "label" || cell.kind === "section") return null;
64
+ return cell.placeholder ?? "-";
65
+ }
66
+ const value = pickValue(cell, item);
67
+ if (!isBlank(value)) return value;
68
+ return item ? cell.placeholder ?? "-" : null;
69
+ }
70
+ function renderCells(cells, items, keyPrefix) {
71
+ return cells.map((cell, index) => {
72
+ const item = items ? items[cell.source ?? 0] ?? null : null;
73
+ return /* @__PURE__ */ jsx(
74
+ "td",
75
+ {
76
+ className: cellClass(cell),
77
+ colSpan: cell.span && cell.span > 1 ? cell.span : void 0,
78
+ rowSpan: cell.rowSpan && cell.rowSpan > 1 ? cell.rowSpan : void 0,
79
+ style: cell.align && cell.align !== "left" ? { textAlign: cell.align } : void 0,
80
+ children: cellContent(cell, item, items !== null)
81
+ },
82
+ `${keyPrefix}-${index}`
83
+ );
84
+ });
85
+ }
86
+ function repeatLength(sources, minRows) {
87
+ const longest = sources.reduce((max, source) => Math.max(max, source.length), 0);
88
+ return Math.max(longest, Math.max(0, Math.floor(minRows ?? 0)));
89
+ }
90
+ function RecordSheetImpl({ columns, title, rows, note }) {
91
+ const width = gridColumns(columns);
92
+ const list = Array.isArray(rows) ? rows : [];
93
+ const body = [];
94
+ list.forEach((row, rowIndex) => {
95
+ if (row?.gap) {
96
+ body.push(
97
+ /* @__PURE__ */ jsx("tr", { className: "erp-recsheet__gap", children: /* @__PURE__ */ jsx("td", { colSpan: width }) }, `gap-${rowIndex}`)
98
+ );
99
+ return;
100
+ }
101
+ const cells = Array.isArray(row?.cells) ? row.cells : [];
102
+ if (cells.length === 0) return;
103
+ if (Array.isArray(row.repeat)) {
104
+ const sources = row.repeat.map((source) => Array.isArray(source) ? source : []);
105
+ const count = repeatLength(sources, row.minRows);
106
+ for (let line = 0; line < count; line += 1) {
107
+ body.push(
108
+ /* @__PURE__ */ jsx("tr", { children: renderCells(
109
+ cells,
110
+ sources.map((source) => source[line] ?? null),
111
+ `rep-${rowIndex}-${line}`
112
+ ) }, `rep-${rowIndex}-${line}`)
113
+ );
114
+ }
115
+ return;
116
+ }
117
+ body.push(/* @__PURE__ */ jsx("tr", { children: renderCells(cells, null, `row-${rowIndex}`) }, `row-${rowIndex}`));
118
+ });
119
+ return /* @__PURE__ */ jsxs("div", { className: "erp-recsheet__scroll", children: [
120
+ /* @__PURE__ */ jsx(RecordSheetStyles, {}),
121
+ /* @__PURE__ */ jsxs("table", { className: "erp-recsheet", children: [
122
+ /* @__PURE__ */ jsx("colgroup", { children: Array.from({ length: width }, (_, index) => /* @__PURE__ */ jsx("col", { style: { width: `${100 / width}%` } }, index)) }),
123
+ /* @__PURE__ */ jsxs("tbody", { children: [
124
+ title ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { className: "erp-recsheet__title", colSpan: width, children: title }) }) : null,
125
+ body
126
+ ] })
127
+ ] }),
128
+ note ? /* @__PURE__ */ jsx("p", { className: "erp-recsheet__note", children: note }) : null
129
+ ] });
130
+ }
131
+ var RecordSheet = boxed(RecordSheetImpl, "RecordSheet");
132
+
133
+ export { RecordSheet };
@@ -1,5 +1,5 @@
1
- import { includesLooseSearch } from './chunk-GUFIEWW2.js';
2
- import { OverlayLayer } from './chunk-IY7SMV6V.js';
1
+ import { includesLooseSearch } from './chunk-U7ZK7WYH.js';
2
+ import { OverlayLayer } from './chunk-XKLUCUVA.js';
3
3
  import { Button, Dialog, Input, Tag, Checkbox } from './chunk-WIDDHR7L.js';
4
4
  import { boxed } from './chunk-SAN4IX6N.js';
5
5
  import React, { useMemo, useState, useEffect } from 'react';
@@ -1,4 +1,4 @@
1
- import { DateRangePicker } from './chunk-IY7SMV6V.js';
1
+ import { DateRangePicker } from './chunk-XKLUCUVA.js';
2
2
  import { SegmentedControl, Select } from './chunk-WIDDHR7L.js';
3
3
  import { boxed } from './chunk-SAN4IX6N.js';
4
4
  import { useState, useMemo } from 'react';
@@ -1,4 +1,4 @@
1
- import { includesLooseSearch } from './chunk-GUFIEWW2.js';
1
+ import { includesLooseSearch } from './chunk-U7ZK7WYH.js';
2
2
  import { Icon, Button, Input, Spinner } from './chunk-WIDDHR7L.js';
3
3
  import { boxed } from './chunk-SAN4IX6N.js';
4
4
  import { useId, useRef, useState, useEffect, useMemo } from 'react';
@@ -1,4 +1,4 @@
1
- import { useOverlayLayer, DateRangePicker } from './chunk-IY7SMV6V.js';
1
+ import { useOverlayLayer, DateRangePicker } from './chunk-XKLUCUVA.js';
2
2
  import { Input, Button, IconButton, Tag, Field, Checkbox } from './chunk-WIDDHR7L.js';
3
3
  import { useRef, useEffect, useState, useMemo } from 'react';
4
4
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';