@seedgrid/fe-components 2026.3.9 → 2026.3.11

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.
Files changed (46) hide show
  1. package/dist/digits/segment-digit/SgSegmentDigit.d.ts +15 -0
  2. package/dist/digits/segment-digit/SgSegmentDigit.d.ts.map +1 -0
  3. package/dist/digits/segment-digit/SgSegmentDigit.js +96 -0
  4. package/dist/digits/segment-digit/index.d.ts +3 -0
  5. package/dist/digits/segment-digit/index.d.ts.map +1 -0
  6. package/dist/digits/segment-digit/index.js +1 -0
  7. package/dist/digits/seven-segment-digit/SgSevenSegmentDigit.d.ts +36 -0
  8. package/dist/digits/seven-segment-digit/SgSevenSegmentDigit.d.ts.map +1 -0
  9. package/dist/digits/seven-segment-digit/SgSevenSegmentDigit.js +123 -0
  10. package/dist/digits/seven-segment-digit/index.d.ts +3 -0
  11. package/dist/digits/seven-segment-digit/index.d.ts.map +1 -0
  12. package/dist/digits/seven-segment-digit/index.js +1 -0
  13. package/dist/gadgets/calendar/SgCalendar.d.ts +29 -0
  14. package/dist/gadgets/calendar/SgCalendar.d.ts.map +1 -0
  15. package/dist/gadgets/calendar/SgCalendar.js +248 -0
  16. package/dist/gadgets/calendar/index.d.ts +3 -0
  17. package/dist/gadgets/calendar/index.d.ts.map +1 -0
  18. package/dist/gadgets/calendar/index.js +1 -0
  19. package/dist/gadgets/clock/SgClock.d.ts +4 -1
  20. package/dist/gadgets/clock/SgClock.d.ts.map +1 -1
  21. package/dist/gadgets/clock/SgClock.js +74 -115
  22. package/dist/i18n/en-US.d.ts.map +1 -1
  23. package/dist/i18n/en-US.js +11 -1
  24. package/dist/i18n/es.d.ts.map +1 -1
  25. package/dist/i18n/es.js +11 -1
  26. package/dist/i18n/pt-BR.d.ts.map +1 -1
  27. package/dist/i18n/pt-BR.js +11 -1
  28. package/dist/i18n/pt-PT.d.ts.map +1 -1
  29. package/dist/i18n/pt-PT.js +11 -1
  30. package/dist/index.d.ts +11 -1
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +5 -0
  33. package/dist/inputs/SgDatatable.d.ts +104 -0
  34. package/dist/inputs/SgDatatable.d.ts.map +1 -0
  35. package/dist/inputs/SgDatatable.js +441 -0
  36. package/dist/layout/SgCard.d.ts +12 -0
  37. package/dist/layout/SgCard.d.ts.map +1 -1
  38. package/dist/layout/SgCard.js +228 -12
  39. package/dist/overlay/SgConfirmationDialog.d.ts +34 -0
  40. package/dist/overlay/SgConfirmationDialog.d.ts.map +1 -0
  41. package/dist/overlay/SgConfirmationDialog.js +81 -0
  42. package/dist/overlay/SgDialog.d.ts +5 -0
  43. package/dist/overlay/SgDialog.d.ts.map +1 -1
  44. package/dist/overlay/SgDialog.js +28 -3
  45. package/dist/sandbox.cjs +84 -84
  46. package/package.json +1 -1
@@ -0,0 +1,441 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import React from "react";
4
+ import { SgButton } from "../buttons/SgButton";
5
+ import { SgGroupBox } from "../layout/SgGroupBox";
6
+ import { t, useComponentsI18n } from "../i18n";
7
+ function cn(...parts) {
8
+ return parts.filter(Boolean).join(" ");
9
+ }
10
+ function toCssSize(value) {
11
+ if (value === undefined || value === null)
12
+ return undefined;
13
+ return typeof value === "number" ? `${value}px` : value;
14
+ }
15
+ function normalizeFilters(filters) {
16
+ if (!filters)
17
+ return {};
18
+ return Object.entries(filters).reduce((acc, [field, value]) => {
19
+ const text = String(value ?? "").trim();
20
+ if (!text)
21
+ return acc;
22
+ acc[field] = text;
23
+ return acc;
24
+ }, {});
25
+ }
26
+ function getFieldValue(row, field) {
27
+ if (!field)
28
+ return undefined;
29
+ if (!field.includes("."))
30
+ return row[field];
31
+ const chunks = field.split(".");
32
+ let current = row;
33
+ for (const chunk of chunks) {
34
+ if (current === null || current === undefined)
35
+ return undefined;
36
+ if (typeof current !== "object")
37
+ return undefined;
38
+ current = current[chunk];
39
+ }
40
+ return current;
41
+ }
42
+ function normalizeText(value) {
43
+ if (value === null || value === undefined)
44
+ return "";
45
+ if (typeof value === "string")
46
+ return value.toLowerCase();
47
+ if (typeof value === "number" || typeof value === "bigint")
48
+ return String(value);
49
+ if (typeof value === "boolean")
50
+ return value ? "true" : "false";
51
+ if (value instanceof Date)
52
+ return value.toISOString().toLowerCase();
53
+ return String(value).toLowerCase();
54
+ }
55
+ function matchesFilter(value, query, mode) {
56
+ const search = query.trim().toLowerCase();
57
+ if (!search)
58
+ return true;
59
+ const source = normalizeText(value);
60
+ if (mode === "startsWith")
61
+ return source.startsWith(search);
62
+ if (mode === "endsWith")
63
+ return source.endsWith(search);
64
+ if (mode === "equals")
65
+ return source === search;
66
+ return source.includes(search);
67
+ }
68
+ function compareValues(a, b) {
69
+ if (a === b)
70
+ return 0;
71
+ if (a === null || a === undefined)
72
+ return -1;
73
+ if (b === null || b === undefined)
74
+ return 1;
75
+ if (typeof a === "number" && typeof b === "number")
76
+ return a - b;
77
+ if (a instanceof Date && b instanceof Date)
78
+ return a.getTime() - b.getTime();
79
+ return String(a).localeCompare(String(b), undefined, {
80
+ sensitivity: "base",
81
+ numeric: true
82
+ });
83
+ }
84
+ function resolveMessage(translated, key, fallback) {
85
+ return translated === key ? fallback : translated;
86
+ }
87
+ function buildPageWindow(currentPage, pageCount, maxButtons = 5) {
88
+ if (pageCount <= 0)
89
+ return [];
90
+ if (pageCount <= maxButtons)
91
+ return Array.from({ length: pageCount }, (_, index) => index + 1);
92
+ const half = Math.floor(maxButtons / 2);
93
+ let start = Math.max(1, currentPage - half);
94
+ let end = start + maxButtons - 1;
95
+ if (end > pageCount) {
96
+ end = pageCount;
97
+ start = end - maxButtons + 1;
98
+ }
99
+ const pages = [];
100
+ for (let page = start; page <= end; page += 1) {
101
+ pages.push(page);
102
+ }
103
+ return pages;
104
+ }
105
+ function getRowIdentity(rowData, dataKey) {
106
+ if (!dataKey)
107
+ return rowData;
108
+ return getFieldValue(rowData, dataKey);
109
+ }
110
+ function rowsAreEqual(rowA, rowB, dataKey) {
111
+ if (!dataKey)
112
+ return rowA === rowB;
113
+ return getRowIdentity(rowA, dataKey) === getRowIdentity(rowB, dataKey);
114
+ }
115
+ function resolveAlignmentClass(align) {
116
+ if (align === "center")
117
+ return "text-center";
118
+ if (align === "right")
119
+ return "text-right";
120
+ return "text-left";
121
+ }
122
+ function SgDatatableBase(props, imperativeRef) {
123
+ const i18n = useComponentsI18n();
124
+ const { id, title, value, columns, dataKey, lazy = false, totalRecords: controlledTotalRecords, paginator = false, rows = 10, first, rowsPerPageOptions, onPage, sortField, sortOrder, onSort, removableSort = true, selectionMode, selection, onSelectionChange, showGlobalFilter = false, globalFilter, globalFilterPlaceholder, onGlobalFilterChange, showColumnFilters = false, filters, onFilter, showClearFiltersButton = false, stripedRows = false, showGridlines = false, hoverableRows = true, loading = false, emptyMessage, className = "", style, tableClassName = "", rowClassName, groupBoxProps = {} } = props;
125
+ const isFirstControlled = first !== undefined;
126
+ const isSortFieldControlled = sortField !== undefined;
127
+ const isSortOrderControlled = sortOrder !== undefined;
128
+ const isSelectionControlled = selection !== undefined;
129
+ const isGlobalFilterControlled = globalFilter !== undefined;
130
+ const isFiltersControlled = filters !== undefined;
131
+ const [internalFirst, setInternalFirst] = React.useState(Math.max(0, first ?? 0));
132
+ const [internalRows, setInternalRows] = React.useState(Math.max(1, rows));
133
+ const [internalSortField, setInternalSortField] = React.useState(sortField ?? null);
134
+ const [internalSortOrder, setInternalSortOrder] = React.useState(sortOrder ?? 0);
135
+ const [internalSelection, setInternalSelection] = React.useState(null);
136
+ const [internalGlobalFilter, setInternalGlobalFilter] = React.useState(globalFilter ?? "");
137
+ const [internalFilters, setInternalFilters] = React.useState(() => normalizeFilters(filters));
138
+ React.useEffect(() => {
139
+ if (!isFirstControlled)
140
+ return;
141
+ setInternalFirst(Math.max(0, first ?? 0));
142
+ }, [isFirstControlled, first]);
143
+ React.useEffect(() => {
144
+ setInternalRows(Math.max(1, rows));
145
+ }, [rows]);
146
+ React.useEffect(() => {
147
+ if (!isSortFieldControlled)
148
+ return;
149
+ setInternalSortField(sortField ?? null);
150
+ }, [isSortFieldControlled, sortField]);
151
+ React.useEffect(() => {
152
+ if (!isSortOrderControlled)
153
+ return;
154
+ setInternalSortOrder(sortOrder ?? 0);
155
+ }, [isSortOrderControlled, sortOrder]);
156
+ React.useEffect(() => {
157
+ if (!isSelectionControlled)
158
+ return;
159
+ setInternalSelection(selection ?? null);
160
+ }, [isSelectionControlled, selection]);
161
+ React.useEffect(() => {
162
+ if (!isGlobalFilterControlled)
163
+ return;
164
+ setInternalGlobalFilter(globalFilter ?? "");
165
+ }, [isGlobalFilterControlled, globalFilter]);
166
+ React.useEffect(() => {
167
+ if (!isFiltersControlled)
168
+ return;
169
+ setInternalFilters(normalizeFilters(filters));
170
+ }, [isFiltersControlled, filters]);
171
+ const visibleColumns = React.useMemo(() => columns.filter((column) => !column.hidden), [columns]);
172
+ const currentFirst = isFirstControlled ? Math.max(0, first ?? 0) : internalFirst;
173
+ const currentRows = Math.max(1, internalRows);
174
+ const currentSortField = isSortFieldControlled ? sortField ?? null : internalSortField;
175
+ const currentSortOrder = isSortOrderControlled ? sortOrder ?? 0 : internalSortOrder;
176
+ const currentSelection = isSelectionControlled ? selection ?? null : internalSelection;
177
+ const currentGlobalFilter = isGlobalFilterControlled ? globalFilter ?? "" : internalGlobalFilter;
178
+ const currentFilters = isFiltersControlled ? normalizeFilters(filters) : internalFilters;
179
+ const globalFilterColumns = React.useMemo(() => visibleColumns.filter((column) => column.field && !column.excludeGlobalFilter), [visibleColumns]);
180
+ const filteredRows = React.useMemo(() => {
181
+ if (lazy)
182
+ return value;
183
+ return value.filter((rowData) => {
184
+ const matchesGlobal = !currentGlobalFilter.trim()
185
+ ? true
186
+ : globalFilterColumns.some((column) => {
187
+ const columnField = column.filterField ?? column.field;
188
+ return matchesFilter(getFieldValue(rowData, columnField), currentGlobalFilter, "contains");
189
+ });
190
+ if (!matchesGlobal)
191
+ return false;
192
+ const columnFilterEntries = Object.entries(currentFilters);
193
+ if (columnFilterEntries.length === 0)
194
+ return true;
195
+ for (const [field, query] of columnFilterEntries) {
196
+ const column = visibleColumns.find((item) => (item.filterField ?? item.field) === field);
197
+ if (!column)
198
+ continue;
199
+ const matchMode = column.filterMatchMode ?? "contains";
200
+ const fieldValue = getFieldValue(rowData, field);
201
+ if (!matchesFilter(fieldValue, query, matchMode))
202
+ return false;
203
+ }
204
+ return true;
205
+ });
206
+ }, [lazy, value, currentGlobalFilter, currentFilters, globalFilterColumns, visibleColumns]);
207
+ const sortedRows = React.useMemo(() => {
208
+ if (lazy)
209
+ return filteredRows;
210
+ if (!currentSortField || currentSortOrder === 0)
211
+ return filteredRows;
212
+ const sortColumn = visibleColumns.find((column) => (column.sortField ?? column.field) === currentSortField);
213
+ const direction = currentSortOrder === -1 ? -1 : 1;
214
+ return [...filteredRows].sort((rowA, rowB) => {
215
+ if (sortColumn?.sortFunction) {
216
+ return sortColumn.sortFunction(rowA, rowB) * direction;
217
+ }
218
+ const sortFieldName = sortColumn?.sortField ?? sortColumn?.field ?? currentSortField;
219
+ const aValue = getFieldValue(rowA, sortFieldName);
220
+ const bValue = getFieldValue(rowB, sortFieldName);
221
+ return compareValues(aValue, bValue) * direction;
222
+ });
223
+ }, [lazy, currentSortField, currentSortOrder, filteredRows, visibleColumns]);
224
+ const resolvedTotalRecords = lazy ? Math.max(0, controlledTotalRecords ?? value.length) : sortedRows.length;
225
+ const pageCount = Math.max(1, Math.ceil(Math.max(resolvedTotalRecords, 1) / currentRows));
226
+ const maxFirst = Math.max(0, (pageCount - 1) * currentRows);
227
+ const safeFirst = Math.min(currentFirst, maxFirst);
228
+ const pagedRows = React.useMemo(() => {
229
+ if (!paginator)
230
+ return sortedRows;
231
+ if (lazy)
232
+ return sortedRows;
233
+ return sortedRows.slice(safeFirst, safeFirst + currentRows);
234
+ }, [paginator, lazy, sortedRows, safeFirst, currentRows]);
235
+ const currentPage = Math.floor(safeFirst / currentRows) + 1;
236
+ const pageWindow = React.useMemo(() => buildPageWindow(currentPage, pageCount, 5), [currentPage, pageCount]);
237
+ const commitPage = React.useCallback((nextFirst, nextRows) => {
238
+ const normalizedRows = Math.max(1, nextRows);
239
+ const normalizedFirst = Math.max(0, nextFirst);
240
+ if (!isFirstControlled)
241
+ setInternalFirst(normalizedFirst);
242
+ setInternalRows(normalizedRows);
243
+ const nextPageCount = Math.max(1, Math.ceil(Math.max(resolvedTotalRecords, 1) / normalizedRows));
244
+ const page = Math.floor(normalizedFirst / normalizedRows) + 1;
245
+ onPage?.({
246
+ first: normalizedFirst,
247
+ rows: normalizedRows,
248
+ page,
249
+ pageCount: nextPageCount,
250
+ totalRecords: resolvedTotalRecords
251
+ });
252
+ }, [isFirstControlled, onPage, resolvedTotalRecords]);
253
+ React.useEffect(() => {
254
+ if (safeFirst === currentFirst)
255
+ return;
256
+ commitPage(safeFirst, currentRows);
257
+ }, [safeFirst, currentFirst, commitPage, currentRows]);
258
+ const commitSort = React.useCallback((nextSortField, nextSortOrder) => {
259
+ if (!isSortFieldControlled)
260
+ setInternalSortField(nextSortField);
261
+ if (!isSortOrderControlled)
262
+ setInternalSortOrder(nextSortOrder);
263
+ onSort?.({ sortField: nextSortField, sortOrder: nextSortOrder });
264
+ }, [isSortFieldControlled, isSortOrderControlled, onSort]);
265
+ const commitSelection = React.useCallback((nextSelection) => {
266
+ if (!isSelectionControlled)
267
+ setInternalSelection(nextSelection);
268
+ onSelectionChange?.(nextSelection);
269
+ }, [isSelectionControlled, onSelectionChange]);
270
+ const commitFilters = React.useCallback((nextFilters, nextGlobalFilter) => {
271
+ if (!isFiltersControlled)
272
+ setInternalFilters(nextFilters);
273
+ if (!isGlobalFilterControlled)
274
+ setInternalGlobalFilter(nextGlobalFilter);
275
+ onGlobalFilterChange?.(nextGlobalFilter);
276
+ onFilter?.({ filters: nextFilters, globalFilter: nextGlobalFilter });
277
+ commitPage(0, currentRows);
278
+ }, [
279
+ isFiltersControlled,
280
+ isGlobalFilterControlled,
281
+ onGlobalFilterChange,
282
+ onFilter,
283
+ commitPage,
284
+ currentRows
285
+ ]);
286
+ const clearFilters = React.useCallback(() => {
287
+ commitFilters({}, "");
288
+ }, [commitFilters]);
289
+ const resetSort = React.useCallback(() => {
290
+ commitSort(null, 0);
291
+ }, [commitSort]);
292
+ const clearSelection = React.useCallback(() => {
293
+ if (selectionMode === "multiple") {
294
+ commitSelection([]);
295
+ return;
296
+ }
297
+ commitSelection(null);
298
+ }, [selectionMode, commitSelection]);
299
+ const goToPage = React.useCallback((page) => {
300
+ const normalizedPage = Math.max(1, Math.min(page, pageCount));
301
+ commitPage((normalizedPage - 1) * currentRows, currentRows);
302
+ }, [commitPage, currentRows, pageCount]);
303
+ React.useImperativeHandle(imperativeRef, () => ({
304
+ getProcessedRows: () => sortedRows,
305
+ getPagedRows: () => pagedRows,
306
+ clearFilters,
307
+ clearSelection,
308
+ resetSort,
309
+ resetPage: () => commitPage(0, currentRows),
310
+ goToPage
311
+ }), [sortedRows, pagedRows, clearFilters, clearSelection, resetSort, commitPage, currentRows, goToPage]);
312
+ const emptyLabel = emptyMessage ?? resolveMessage(t(i18n, "components.datatable.empty"), "components.datatable.empty", "No records found.");
313
+ const loadingLabel = resolveMessage(t(i18n, "components.datatable.loading"), "components.datatable.loading", "Loading data...");
314
+ const globalFilterLabel = globalFilterPlaceholder ?? resolveMessage(t(i18n, "components.datatable.globalFilterPlaceholder"), "components.datatable.globalFilterPlaceholder", "Search in all columns");
315
+ const clearFiltersLabel = resolveMessage(t(i18n, "components.datatable.clearFilters"), "components.datatable.clearFilters", "Clear filters");
316
+ const prevLabel = resolveMessage(t(i18n, "components.datatable.prev"), "components.datatable.prev", "Prev");
317
+ const nextLabel = resolveMessage(t(i18n, "components.datatable.next"), "components.datatable.next", "Next");
318
+ const rowsPerPageLabel = resolveMessage(t(i18n, "components.datatable.rowsPerPage"), "components.datatable.rowsPerPage", "Rows per page");
319
+ const pageReport = resolveMessage(t(i18n, "components.datatable.pageReport", {
320
+ first: resolvedTotalRecords === 0 ? 0 : safeFirst + 1,
321
+ last: Math.min(safeFirst + currentRows, resolvedTotalRecords),
322
+ total: resolvedTotalRecords
323
+ }), "components.datatable.pageReport", `${resolvedTotalRecords === 0 ? 0 : safeFirst + 1} - ${Math.min(safeFirst + currentRows, resolvedTotalRecords)} of ${resolvedTotalRecords}`);
324
+ const shouldShowToolbar = showGlobalFilter || showClearFiltersButton;
325
+ const shouldShowFiltersRow = showColumnFilters && visibleColumns.some((column) => column.filter);
326
+ const hasFooter = visibleColumns.some((column) => column.footer !== undefined);
327
+ const handleHeaderSort = (column) => {
328
+ if (!column.sortable)
329
+ return;
330
+ const nextSortField = column.sortField ?? column.field;
331
+ if (!nextSortField)
332
+ return;
333
+ if (currentSortField !== nextSortField) {
334
+ commitSort(nextSortField, 1);
335
+ return;
336
+ }
337
+ if (currentSortOrder === 1) {
338
+ commitSort(nextSortField, -1);
339
+ return;
340
+ }
341
+ if (currentSortOrder === -1) {
342
+ if (removableSort) {
343
+ commitSort(null, 0);
344
+ }
345
+ else {
346
+ commitSort(nextSortField, 1);
347
+ }
348
+ return;
349
+ }
350
+ commitSort(nextSortField, 1);
351
+ };
352
+ const isRowSelected = React.useCallback((rowData) => {
353
+ if (!selectionMode)
354
+ return false;
355
+ if (selectionMode === "single") {
356
+ return currentSelection !== null && !Array.isArray(currentSelection)
357
+ ? rowsAreEqual(currentSelection, rowData, dataKey)
358
+ : false;
359
+ }
360
+ const list = Array.isArray(currentSelection) ? currentSelection : [];
361
+ return list.some((entry) => rowsAreEqual(entry, rowData, dataKey));
362
+ }, [selectionMode, currentSelection, dataKey]);
363
+ const handleRowSelection = React.useCallback((rowData) => {
364
+ if (!selectionMode)
365
+ return;
366
+ if (selectionMode === "single") {
367
+ if (currentSelection !== null && !Array.isArray(currentSelection) && rowsAreEqual(currentSelection, rowData, dataKey)) {
368
+ commitSelection(null);
369
+ }
370
+ else {
371
+ commitSelection(rowData);
372
+ }
373
+ return;
374
+ }
375
+ const previous = Array.isArray(currentSelection) ? currentSelection : [];
376
+ const exists = previous.some((entry) => rowsAreEqual(entry, rowData, dataKey));
377
+ const next = exists
378
+ ? previous.filter((entry) => !rowsAreEqual(entry, rowData, dataKey))
379
+ : [...previous, rowData];
380
+ commitSelection(next);
381
+ }, [selectionMode, currentSelection, dataKey, commitSelection]);
382
+ const resolvedTitle = (groupBoxProps.title ?? title ?? "").trim() || " ";
383
+ return (_jsx("div", { className: className, style: style, children: _jsx(SgGroupBox, { ...groupBoxProps, title: resolvedTitle, children: _jsxs("div", { className: "space-y-3", children: [shouldShowToolbar ? (_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [showGlobalFilter ? (_jsx("input", { value: currentGlobalFilter, onChange: (event) => commitFilters(currentFilters, event.target.value), placeholder: globalFilterLabel, className: "min-w-[220px] flex-1 rounded-md border border-[rgb(var(--sg-border))] bg-white px-3 py-2 text-sm outline-none focus:ring-2 focus:ring-[rgb(var(--sg-ring))]" })) : null, showClearFiltersButton ? (_jsx(SgButton, { size: "sm", appearance: "outline", onClick: clearFilters, children: clearFiltersLabel })) : null] })) : null, _jsx("div", { className: "relative", children: _jsx("div", { className: "overflow-x-auto rounded-lg border border-[rgb(var(--sg-border))] bg-white", children: _jsxs("table", { id: id, className: cn("min-w-full border-collapse text-sm", tableClassName), children: [_jsxs("thead", { children: [_jsx("tr", { className: "bg-[rgb(var(--sg-primary-50))]", children: visibleColumns.map((column, columnIndex) => {
384
+ const sortCandidate = column.sortField ?? column.field;
385
+ const isSorted = Boolean(sortCandidate) && sortCandidate === currentSortField;
386
+ const sortIcon = !column.sortable
387
+ ? null
388
+ : isSorted
389
+ ? currentSortOrder === 1
390
+ ? " ^"
391
+ : currentSortOrder === -1
392
+ ? " v"
393
+ : " <>"
394
+ : " <>";
395
+ return (_jsx("th", { className: cn("px-3 py-2 font-semibold text-[rgb(var(--sg-text))]", resolveAlignmentClass(column.align), showGridlines ? "border-b border-r border-[rgb(var(--sg-border))] last:border-r-0" : "border-b border-[rgb(var(--sg-border))]", column.headerClassName, column.className), style: { width: toCssSize(column.width), minWidth: toCssSize(column.minWidth) }, children: column.sortable ? (_jsxs("button", { type: "button", onClick: () => handleHeaderSort(column), className: cn("inline-flex items-center gap-1 font-semibold", resolveAlignmentClass(column.align), "text-[rgb(var(--sg-text))] hover:text-[rgb(var(--sg-primary-600))]"), children: [_jsx("span", { children: column.header }), _jsx("span", { "aria-hidden": "true", children: sortIcon })] })) : (_jsx("span", { children: column.header })) }, `head-${column.field ?? columnIndex}`));
396
+ }) }), shouldShowFiltersRow ? (_jsx("tr", { children: visibleColumns.map((column, columnIndex) => {
397
+ const filterField = column.filterField ?? column.field;
398
+ const filterValue = filterField ? currentFilters[filterField] ?? "" : "";
399
+ return (_jsx("th", { className: cn("px-3 py-2 align-top", showGridlines ? "border-b border-r border-[rgb(var(--sg-border))] last:border-r-0" : "border-b border-[rgb(var(--sg-border))]"), children: column.filter && filterField ? (_jsx("input", { value: filterValue, onChange: (event) => {
400
+ const nextFilters = {
401
+ ...currentFilters,
402
+ [filterField]: event.target.value
403
+ };
404
+ const normalized = normalizeFilters(nextFilters);
405
+ commitFilters(normalized, currentGlobalFilter);
406
+ }, placeholder: column.filterPlaceholder ?? `Filter ${String(column.header ?? "")}`, className: "w-full rounded-md border border-[rgb(var(--sg-border))] bg-white px-2 py-1 text-xs outline-none focus:ring-2 focus:ring-[rgb(var(--sg-ring))]" })) : null }, `filter-${column.field ?? columnIndex}`));
407
+ }) })) : null] }), _jsx("tbody", { children: loading ? (_jsx("tr", { children: _jsx("td", { colSpan: Math.max(1, visibleColumns.length), className: "px-4 py-6 text-center text-sm text-[rgb(var(--sg-muted))]", children: loadingLabel }) })) : pagedRows.length === 0 ? (_jsx("tr", { children: _jsx("td", { colSpan: Math.max(1, visibleColumns.length), className: "px-4 py-6 text-center text-sm text-[rgb(var(--sg-muted))]", children: emptyLabel }) })) : (pagedRows.map((rowData, rowIndex) => {
408
+ const absoluteRowIndex = safeFirst + rowIndex;
409
+ const selected = isRowSelected(rowData);
410
+ const customRowClass = typeof rowClassName === "function" ? rowClassName(rowData, absoluteRowIndex) : rowClassName;
411
+ return (_jsx("tr", { "aria-selected": selected || undefined, onClick: (event) => {
412
+ const target = event.target;
413
+ if (target.closest("button,a,input,select,textarea,label,[data-sg-stop-row-select='true']")) {
414
+ return;
415
+ }
416
+ handleRowSelection(rowData);
417
+ }, className: cn(selectionMode ? "cursor-pointer" : "", stripedRows && absoluteRowIndex % 2 === 1 ? "bg-[rgb(var(--sg-primary-50))]" : "", hoverableRows ? "hover:bg-[rgb(var(--sg-primary-100))]" : "", selected ? "bg-[rgb(var(--sg-primary-100))]" : "", customRowClass), children: visibleColumns.map((column, columnIndex) => {
418
+ const cellField = column.field;
419
+ const cellValue = getFieldValue(rowData, cellField);
420
+ const customCellClass = typeof column.bodyClassName === "function"
421
+ ? column.bodyClassName(rowData, absoluteRowIndex)
422
+ : column.bodyClassName;
423
+ const renderedValue = column.body
424
+ ? column.body(rowData, {
425
+ rowData,
426
+ rowIndex: absoluteRowIndex,
427
+ field: cellField,
428
+ value: cellValue
429
+ })
430
+ : (_jsx("span", { children: cellValue === null || cellValue === undefined ? "-" : String(cellValue) }));
431
+ return (_jsx("td", { className: cn("px-3 py-2 text-[rgb(var(--sg-text))]", resolveAlignmentClass(column.align), showGridlines ? "border-b border-r border-[rgb(var(--sg-border))] last:border-r-0" : "border-b border-[rgb(var(--sg-border))]", column.className, customCellClass), style: { width: toCssSize(column.width), minWidth: toCssSize(column.minWidth) }, children: renderedValue }, `cell-${cellField ?? columnIndex}-${absoluteRowIndex}`));
432
+ }) }, String(getRowIdentity(rowData, dataKey) ?? absoluteRowIndex)));
433
+ })) }), hasFooter ? (_jsx("tfoot", { children: _jsx("tr", { className: "bg-[rgb(var(--sg-primary-50))]", children: visibleColumns.map((column, columnIndex) => {
434
+ const footerValue = typeof column.footer === "function" ? column.footer(sortedRows) : column.footer;
435
+ return (_jsx("td", { className: cn("px-3 py-2 font-semibold text-[rgb(var(--sg-text))]", resolveAlignmentClass(column.align), showGridlines ? "border-t border-r border-[rgb(var(--sg-border))] last:border-r-0" : "border-t border-[rgb(var(--sg-border))]", column.className), style: { width: toCssSize(column.width), minWidth: toCssSize(column.minWidth) }, children: footerValue ?? null }, `foot-${column.field ?? columnIndex}`));
436
+ }) }) })) : null] }) }) }), paginator ? (_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx(SgButton, { size: "sm", appearance: "outline", disabled: currentPage <= 1, onClick: () => goToPage(currentPage - 1), children: prevLabel }), pageWindow.map((pageNumber) => (_jsx(SgButton, { size: "sm", appearance: pageNumber === currentPage ? "solid" : "outline", onClick: () => goToPage(pageNumber), children: String(pageNumber) }, `page-${pageNumber}`))), _jsx(SgButton, { size: "sm", appearance: "outline", disabled: currentPage >= pageCount, onClick: () => goToPage(currentPage + 1), children: nextLabel }), _jsx("span", { className: "min-w-[180px] text-xs text-[rgb(var(--sg-muted))] md:ml-auto", children: pageReport }), rowsPerPageOptions && rowsPerPageOptions.length > 0 ? (_jsxs("label", { className: "ml-auto inline-flex items-center gap-2 text-xs text-[rgb(var(--sg-muted))] md:ml-0", children: [_jsx("span", { children: rowsPerPageLabel }), _jsx("select", { value: currentRows, onChange: (event) => {
437
+ const nextRows = Number(event.target.value) || currentRows;
438
+ commitPage(0, nextRows);
439
+ }, className: "rounded-md border border-[rgb(var(--sg-border))] bg-white px-2 py-1 text-xs", children: rowsPerPageOptions.map((option) => (_jsx("option", { value: option, children: option }, option))) })] })) : null] })) : null] }) }) }));
440
+ }
441
+ export const SgDatatable = React.forwardRef(SgDatatableBase);
@@ -1,11 +1,18 @@
1
1
  import * as React from "react";
2
2
  export type SgCardVariant = "default" | "outlined" | "flat" | "elevated";
3
3
  export type SgCardSize = "sm" | "md" | "lg";
4
+ export type SgCardPosition = {
5
+ x: number;
6
+ y: number;
7
+ };
4
8
  export type SgCardProps = Omit<React.HTMLAttributes<HTMLElement>, "title" | "onClick"> & {
5
9
  className?: string;
6
10
  headerClassName?: string;
7
11
  bodyClassName?: string;
8
12
  footerClassName?: string;
13
+ bgColor?: string;
14
+ bgColorTitle?: string;
15
+ bgColorFooter?: string;
9
16
  cardStyle?: SgCardVariant;
10
17
  size?: SgCardSize;
11
18
  leading?: React.ReactNode;
@@ -23,8 +30,13 @@ export type SgCardProps = Omit<React.HTMLAttributes<HTMLElement>, "title" | "onC
23
30
  open?: boolean;
24
31
  onOpenChange?: (open: boolean) => void;
25
32
  collapseIcon?: React.ReactNode;
33
+ collapseIconSize?: number;
26
34
  collapseToggleAlign?: "left" | "right";
27
35
  toggleOnHeaderClick?: boolean;
36
+ draggable?: boolean;
37
+ defaultPosition?: SgCardPosition;
38
+ dragPersistKey?: string;
39
+ onPositionChange?: (position: SgCardPosition) => void;
28
40
  onClick?: React.MouseEventHandler<HTMLElement>;
29
41
  children?: React.ReactNode;
30
42
  };
@@ -1 +1 @@
1
- {"version":3,"file":"SgCard.d.ts","sourceRoot":"","sources":["../../src/layout/SgCard.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,GAAG,UAAU,CAAC;AACzE,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE5C,MAAM,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,WAAW,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC,GAAG;IACvF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,IAAI,CAAC,EAAE,UAAU,CAAC;IAElB,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAE1B,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAEzB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC/B,mBAAmB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACvC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAE9B,OAAO,CAAC,EAAE,KAAK,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC;IAC/C,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B,CAAC;AA0BF,wBAAgB,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,WAAW,CAAC,2CA4OlD;yBA5Oe,MAAM"}
1
+ {"version":3,"file":"SgCard.d.ts","sourceRoot":"","sources":["../../src/layout/SgCard.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,GAAG,UAAU,CAAC;AACzE,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAC5C,MAAM,MAAM,cAAc,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtD,MAAM,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,WAAW,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC,GAAG;IACvF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,IAAI,CAAC,EAAE,UAAU,CAAC;IAElB,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAE1B,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAEzB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC/B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACvC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,eAAe,CAAC,EAAE,cAAc,CAAC;IACjC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,cAAc,KAAK,IAAI,CAAC;IAEtD,OAAO,CAAC,EAAE,KAAK,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC;IAC/C,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B,CAAC;AAiEF,wBAAgB,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,WAAW,CAAC,2CAielD;yBAjee,MAAM"}