@almadar/ui 5.8.1 → 5.9.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.
@@ -29236,6 +29236,311 @@ var init_Lightbox = __esm({
29236
29236
  Lightbox.displayName = "Lightbox";
29237
29237
  }
29238
29238
  });
29239
+ function columnLabel(col) {
29240
+ return col.header ?? col.label ?? col.key.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
29241
+ }
29242
+ function statusVariant4(value) {
29243
+ const v = value.toLowerCase();
29244
+ if (["active", "completed", "done", "approved", "published", "resolved", "open", "online", "ok"].includes(v)) return "success";
29245
+ if (["pending", "in_progress", "in-progress", "review", "draft", "processing", "warn", "warning"].includes(v)) return "warning";
29246
+ if (["inactive", "deleted", "rejected", "failed", "error", "blocked", "closed", "offline"].includes(v)) return "error";
29247
+ if (["new", "created", "scheduled", "queued", "info"].includes(v)) return "info";
29248
+ return "default";
29249
+ }
29250
+ function formatCell(value, format) {
29251
+ if (value === void 0 || value === null) return "";
29252
+ switch (format) {
29253
+ case "date": {
29254
+ const d = new Date(String(value));
29255
+ return isNaN(d.getTime()) ? String(value) : d.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
29256
+ }
29257
+ case "currency":
29258
+ return typeof value === "number" ? `$${value.toFixed(2)}` : String(value);
29259
+ case "number":
29260
+ return typeof value === "number" ? value.toLocaleString() : String(value);
29261
+ case "percent":
29262
+ return typeof value === "number" ? `${Math.round(value)}%` : String(value);
29263
+ case "boolean":
29264
+ return value ? "Yes" : "No";
29265
+ default:
29266
+ return String(value);
29267
+ }
29268
+ }
29269
+ function groupData2(items, field) {
29270
+ const groups = /* @__PURE__ */ new Map();
29271
+ for (const item of items) {
29272
+ const key = String(getNestedValue(item, field) ?? "");
29273
+ const group = groups.get(key);
29274
+ if (group) group.push(item);
29275
+ else groups.set(key, [item]);
29276
+ }
29277
+ return Array.from(groups.entries()).map(([label, groupItems]) => ({ label, items: groupItems }));
29278
+ }
29279
+ function TableView({
29280
+ entity,
29281
+ columns,
29282
+ fields,
29283
+ itemActions,
29284
+ selectable = false,
29285
+ selectEvent,
29286
+ selectedIds,
29287
+ sortEvent,
29288
+ sortColumn,
29289
+ sortDirection,
29290
+ className,
29291
+ emptyMessage,
29292
+ isLoading = false,
29293
+ error = null,
29294
+ groupBy,
29295
+ pageSize = 0,
29296
+ children,
29297
+ renderItem: _schemaRenderItem,
29298
+ look = "dense",
29299
+ // DnD props consumed by useDataDnd.
29300
+ dragGroup,
29301
+ accepts,
29302
+ sortable,
29303
+ dropEvent,
29304
+ reorderEvent,
29305
+ positionEvent,
29306
+ dndItemIdField,
29307
+ dndRoot
29308
+ }) {
29309
+ const eventBus = useEventBus();
29310
+ const { t } = useTranslate();
29311
+ const [visibleCount, setVisibleCount] = React86__namespace.default.useState(pageSize > 0 ? pageSize : Infinity);
29312
+ const [localSelected, setLocalSelected] = React86__namespace.default.useState(/* @__PURE__ */ new Set());
29313
+ const colDefs = columns ?? fields ?? [];
29314
+ const allDataRaw = Array.isArray(entity) ? entity : entity ? [entity] : [];
29315
+ const dnd = useDataDnd({
29316
+ items: allDataRaw,
29317
+ layout: "list",
29318
+ dragGroup,
29319
+ accepts,
29320
+ sortable,
29321
+ dropEvent,
29322
+ reorderEvent,
29323
+ positionEvent,
29324
+ dndItemIdField,
29325
+ dndRoot
29326
+ });
29327
+ const ordered = dnd.orderedItems;
29328
+ const data = pageSize > 0 ? ordered.slice(0, visibleCount) : ordered;
29329
+ const hasMore = pageSize > 0 && visibleCount < ordered.length;
29330
+ const hasRenderProp = typeof children === "function";
29331
+ const idField = dndItemIdField ?? "id";
29332
+ const selected = selectedIds ? new Set(selectedIds) : localSelected;
29333
+ const emitSelection = (next) => {
29334
+ if (!selectedIds) setLocalSelected(next);
29335
+ if (selectEvent) {
29336
+ const payload = { selectedIds: Array.from(next) };
29337
+ eventBus.emit(`UI:${selectEvent}`, payload);
29338
+ }
29339
+ };
29340
+ const allSelected = selectable && data.length > 0 && data.every((r, i) => selected.has(String(r[idField] ?? i)));
29341
+ const toggleAll = () => {
29342
+ if (allSelected) emitSelection(/* @__PURE__ */ new Set());
29343
+ else emitSelection(new Set(data.map((r, i) => String(r[idField] ?? i))));
29344
+ };
29345
+ const toggleRow = (id) => {
29346
+ const next = new Set(selected);
29347
+ if (next.has(id)) next.delete(id);
29348
+ else next.add(id);
29349
+ emitSelection(next);
29350
+ };
29351
+ const handleSort = (col) => {
29352
+ if (!col.sortable || !sortEvent) return;
29353
+ const dir = sortColumn === (col.field ?? col.key) && sortDirection === "asc" ? "desc" : "asc";
29354
+ eventBus.emit(`UI:${sortEvent}`, { column: col.field ?? col.key, direction: dir });
29355
+ };
29356
+ const handleActionClick = (action, row) => (e) => {
29357
+ e.stopPropagation();
29358
+ const payload = {
29359
+ id: row.id,
29360
+ row
29361
+ };
29362
+ eventBus.emit(`UI:${action.event}`, payload);
29363
+ };
29364
+ if (isLoading) {
29365
+ return /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", color: "secondary", children: t("loading.items") || "Loading\u2026" }) });
29366
+ }
29367
+ if (error) {
29368
+ return /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", color: "error", children: error.message }) });
29369
+ }
29370
+ if (data.length === 0) {
29371
+ const emptyNode = /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "text-center py-12", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", color: "secondary", children: emptyMessage || t("empty.noItems") || "No records" }) });
29372
+ return dnd.enabled ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: dnd.wrapContainer(emptyNode) }) : emptyNode;
29373
+ }
29374
+ const lk = LOOKS[look];
29375
+ const header = /* @__PURE__ */ jsxRuntime.jsxs(
29376
+ Box,
29377
+ {
29378
+ role: "row",
29379
+ className: cn(
29380
+ "flex items-center gap-3 sticky top-0 z-10",
29381
+ "bg-[var(--color-surface-subtle)] border-b border-[var(--color-table-border)]",
29382
+ "text-[var(--color-text-muted)] uppercase text-xs font-semibold tracking-wide",
29383
+ lk.headPad
29384
+ ),
29385
+ children: [
29386
+ selectable && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-8 flex-shrink-0 flex items-center", children: /* @__PURE__ */ jsxRuntime.jsx(Checkbox, { checked: allSelected, onChange: toggleAll, "aria-label": "Select all rows" }) }),
29387
+ colDefs.map((col) => {
29388
+ const active = sortColumn === (col.field ?? col.key);
29389
+ return /* @__PURE__ */ jsxRuntime.jsxs(
29390
+ Box,
29391
+ {
29392
+ role: "columnheader",
29393
+ onClick: () => handleSort(col),
29394
+ className: cn(
29395
+ "flex items-center gap-1 min-w-0",
29396
+ col.width ?? "flex-1",
29397
+ alignClass[col.align ?? "left"],
29398
+ col.sortable && sortEvent && "cursor-pointer select-none hover:text-foreground"
29399
+ ),
29400
+ children: [
29401
+ col.icon && /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: col.icon, size: "xs" }),
29402
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: columnLabel(col) }),
29403
+ col.sortable && sortEvent && /* @__PURE__ */ jsxRuntime.jsx(
29404
+ Icon,
29405
+ {
29406
+ name: active ? sortDirection === "asc" ? "chevron-up" : "chevron-down" : "chevrons-up-down",
29407
+ size: "xs",
29408
+ className: cn("flex-shrink-0", active ? "text-foreground" : "opacity-40")
29409
+ }
29410
+ )
29411
+ ]
29412
+ },
29413
+ col.key
29414
+ );
29415
+ }),
29416
+ itemActions && itemActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-px flex-shrink-0", "aria-hidden": true })
29417
+ ]
29418
+ }
29419
+ );
29420
+ const renderRow = (row, index) => {
29421
+ const id = String(row[idField] ?? index);
29422
+ const rowInner = /* @__PURE__ */ jsxRuntime.jsxs(
29423
+ Box,
29424
+ {
29425
+ role: "row",
29426
+ "data-entity-row": true,
29427
+ "data-entity-id": id,
29428
+ className: cn(
29429
+ "group flex items-center gap-3 transition-colors duration-fast",
29430
+ lk.rowPad,
29431
+ lk.divider && "border-b border-[var(--color-table-border)]",
29432
+ lk.striped && index % 2 === 1 && "bg-[var(--color-surface-subtle)]",
29433
+ "hover:bg-[var(--color-surface-subtle)]",
29434
+ look === "bordered" && "[&>*]:border-r [&>*]:border-[var(--color-table-border)] [&>*:last-child]:border-r-0"
29435
+ ),
29436
+ children: [
29437
+ selectable && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "w-8 flex-shrink-0 flex items-center", children: /* @__PURE__ */ jsxRuntime.jsx(
29438
+ Checkbox,
29439
+ {
29440
+ checked: selected.has(id),
29441
+ onChange: () => toggleRow(id),
29442
+ "aria-label": `Select row ${id}`
29443
+ }
29444
+ ) }),
29445
+ hasRenderProp ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "flex-1 min-w-0", children: children(row, index) }) : colDefs.map((col) => {
29446
+ const raw = getNestedValue(row, col.field ?? col.key);
29447
+ const cellBase = cn(
29448
+ "flex items-center min-w-0",
29449
+ col.width ?? "flex-1",
29450
+ alignClass[col.align ?? "left"],
29451
+ weightClass[col.weight ?? "normal"],
29452
+ col.className
29453
+ );
29454
+ if (col.format === "badge" && raw != null && raw !== "") {
29455
+ return /* @__PURE__ */ jsxRuntime.jsx(Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: statusVariant4(String(raw)), size: "sm", children: String(raw) }) }, col.key);
29456
+ }
29457
+ return /* @__PURE__ */ jsxRuntime.jsx(Box, { role: "cell", className: cn(cellBase, "truncate block"), children: formatCell(raw, col.format) }, col.key);
29458
+ }),
29459
+ itemActions && itemActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(HStack, { gap: "xs", className: "flex-shrink-0 opacity-60 group-hover:opacity-100 transition-opacity", children: itemActions.map((action, i) => /* @__PURE__ */ jsxRuntime.jsxs(
29460
+ Button,
29461
+ {
29462
+ variant: action.variant ?? "ghost",
29463
+ size: "sm",
29464
+ onClick: handleActionClick(action, row),
29465
+ "data-testid": `action-${action.event}`,
29466
+ "data-row-id": String(row.id),
29467
+ className: cn(action.variant === "danger" && "text-error hover:bg-error/10"),
29468
+ children: [
29469
+ action.icon && /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: action.icon, size: "xs", className: "mr-1" }),
29470
+ action.label
29471
+ ]
29472
+ },
29473
+ i
29474
+ )) })
29475
+ ]
29476
+ }
29477
+ );
29478
+ return dnd.isZone ? /* @__PURE__ */ jsxRuntime.jsx(dnd.SortableItem, { id: row[idField] ?? id, children: rowInner }, id) : /* @__PURE__ */ jsxRuntime.jsx(React86__namespace.default.Fragment, { children: rowInner }, id);
29479
+ };
29480
+ const items = data.map((row) => row);
29481
+ const groups = groupBy ? groupData2(items, groupBy) : [{ label: "", items }];
29482
+ let runningIndex = 0;
29483
+ const body = /* @__PURE__ */ jsxRuntime.jsx(Box, { role: "rowgroup", children: groups.map((group, gi) => /* @__PURE__ */ jsxRuntime.jsxs(React86__namespace.default.Fragment, { children: [
29484
+ group.label && /* @__PURE__ */ jsxRuntime.jsx(Divider, { label: group.label, className: gi > 0 ? "mt-3" : "mt-0" }),
29485
+ group.items.map((row) => renderRow(row, runningIndex++))
29486
+ ] }, gi)) });
29487
+ return /* @__PURE__ */ jsxRuntime.jsxs(
29488
+ Box,
29489
+ {
29490
+ role: "table",
29491
+ className: cn("w-full text-sm", className),
29492
+ children: [
29493
+ header,
29494
+ dnd.wrapContainer(body),
29495
+ hasMore && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "flex justify-center py-3", children: /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "ghost", size: "sm", onClick: () => setVisibleCount((p2) => p2 + (pageSize || 5)), children: [
29496
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "chevron-down", size: "xs", className: "mr-1" }),
29497
+ t("common.showMore"),
29498
+ " (",
29499
+ ordered.length - visibleCount,
29500
+ " remaining)"
29501
+ ] }) })
29502
+ ]
29503
+ }
29504
+ );
29505
+ }
29506
+ var alignClass, weightClass, LOOKS;
29507
+ var init_TableView = __esm({
29508
+ "components/molecules/TableView.tsx"() {
29509
+ "use client";
29510
+ init_cn();
29511
+ init_getNestedValue();
29512
+ init_useEventBus();
29513
+ init_useTranslate();
29514
+ init_Box();
29515
+ init_Stack();
29516
+ init_Typography();
29517
+ init_Badge();
29518
+ init_Button();
29519
+ init_Icon();
29520
+ init_Checkbox();
29521
+ init_Divider();
29522
+ init_useDataDnd();
29523
+ logger.createLogger("almadar:ui:table-view");
29524
+ alignClass = {
29525
+ left: "justify-start text-left",
29526
+ center: "justify-center text-center",
29527
+ right: "justify-end text-right"
29528
+ };
29529
+ weightClass = {
29530
+ normal: "",
29531
+ medium: "font-medium",
29532
+ semibold: "font-semibold"
29533
+ };
29534
+ LOOKS = {
29535
+ dense: { rowPad: "px-3 py-2", headPad: "px-3 py-2", striped: false, divider: true },
29536
+ spacious: { rowPad: "px-5 py-4", headPad: "px-5 py-3", striped: false, divider: true },
29537
+ striped: { rowPad: "px-3 py-2", headPad: "px-3 py-2", striped: true, divider: false },
29538
+ borderless: { rowPad: "px-3 py-2", headPad: "px-3 py-2", striped: false, divider: false },
29539
+ bordered: { rowPad: "px-3 py-2", headPad: "px-3 py-2", striped: false, divider: true }
29540
+ };
29541
+ TableView.displayName = "TableView";
29542
+ }
29543
+ });
29239
29544
  function formatNumber(value, format) {
29240
29545
  if (value == null) return "0";
29241
29546
  const v = typeof value === "number" ? value : value;
@@ -46293,6 +46598,7 @@ var init_component_registry_generated = __esm({
46293
46598
  init_Switch();
46294
46599
  init_TabbedContainer();
46295
46600
  init_Table();
46601
+ init_TableView();
46296
46602
  init_Tabs();
46297
46603
  init_TagCloud();
46298
46604
  init_TagInput();
@@ -46610,6 +46916,7 @@ var init_component_registry_generated = __esm({
46610
46916
  "Switch": Switch,
46611
46917
  "TabbedContainer": TabbedContainer,
46612
46918
  "Table": Table,
46919
+ "TableView": TableView,
46613
46920
  "Tabs": Tabs,
46614
46921
  "TagCloud": TagCloud,
46615
46922
  "TagInput": TagInput,
@@ -29187,6 +29187,311 @@ var init_Lightbox = __esm({
29187
29187
  Lightbox.displayName = "Lightbox";
29188
29188
  }
29189
29189
  });
29190
+ function columnLabel(col) {
29191
+ return col.header ?? col.label ?? col.key.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
29192
+ }
29193
+ function statusVariant4(value) {
29194
+ const v = value.toLowerCase();
29195
+ if (["active", "completed", "done", "approved", "published", "resolved", "open", "online", "ok"].includes(v)) return "success";
29196
+ if (["pending", "in_progress", "in-progress", "review", "draft", "processing", "warn", "warning"].includes(v)) return "warning";
29197
+ if (["inactive", "deleted", "rejected", "failed", "error", "blocked", "closed", "offline"].includes(v)) return "error";
29198
+ if (["new", "created", "scheduled", "queued", "info"].includes(v)) return "info";
29199
+ return "default";
29200
+ }
29201
+ function formatCell(value, format) {
29202
+ if (value === void 0 || value === null) return "";
29203
+ switch (format) {
29204
+ case "date": {
29205
+ const d = new Date(String(value));
29206
+ return isNaN(d.getTime()) ? String(value) : d.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
29207
+ }
29208
+ case "currency":
29209
+ return typeof value === "number" ? `$${value.toFixed(2)}` : String(value);
29210
+ case "number":
29211
+ return typeof value === "number" ? value.toLocaleString() : String(value);
29212
+ case "percent":
29213
+ return typeof value === "number" ? `${Math.round(value)}%` : String(value);
29214
+ case "boolean":
29215
+ return value ? "Yes" : "No";
29216
+ default:
29217
+ return String(value);
29218
+ }
29219
+ }
29220
+ function groupData2(items, field) {
29221
+ const groups = /* @__PURE__ */ new Map();
29222
+ for (const item of items) {
29223
+ const key = String(getNestedValue(item, field) ?? "");
29224
+ const group = groups.get(key);
29225
+ if (group) group.push(item);
29226
+ else groups.set(key, [item]);
29227
+ }
29228
+ return Array.from(groups.entries()).map(([label, groupItems]) => ({ label, items: groupItems }));
29229
+ }
29230
+ function TableView({
29231
+ entity,
29232
+ columns,
29233
+ fields,
29234
+ itemActions,
29235
+ selectable = false,
29236
+ selectEvent,
29237
+ selectedIds,
29238
+ sortEvent,
29239
+ sortColumn,
29240
+ sortDirection,
29241
+ className,
29242
+ emptyMessage,
29243
+ isLoading = false,
29244
+ error = null,
29245
+ groupBy,
29246
+ pageSize = 0,
29247
+ children,
29248
+ renderItem: _schemaRenderItem,
29249
+ look = "dense",
29250
+ // DnD props consumed by useDataDnd.
29251
+ dragGroup,
29252
+ accepts,
29253
+ sortable,
29254
+ dropEvent,
29255
+ reorderEvent,
29256
+ positionEvent,
29257
+ dndItemIdField,
29258
+ dndRoot
29259
+ }) {
29260
+ const eventBus = useEventBus();
29261
+ const { t } = useTranslate();
29262
+ const [visibleCount, setVisibleCount] = React86__default.useState(pageSize > 0 ? pageSize : Infinity);
29263
+ const [localSelected, setLocalSelected] = React86__default.useState(/* @__PURE__ */ new Set());
29264
+ const colDefs = columns ?? fields ?? [];
29265
+ const allDataRaw = Array.isArray(entity) ? entity : entity ? [entity] : [];
29266
+ const dnd = useDataDnd({
29267
+ items: allDataRaw,
29268
+ layout: "list",
29269
+ dragGroup,
29270
+ accepts,
29271
+ sortable,
29272
+ dropEvent,
29273
+ reorderEvent,
29274
+ positionEvent,
29275
+ dndItemIdField,
29276
+ dndRoot
29277
+ });
29278
+ const ordered = dnd.orderedItems;
29279
+ const data = pageSize > 0 ? ordered.slice(0, visibleCount) : ordered;
29280
+ const hasMore = pageSize > 0 && visibleCount < ordered.length;
29281
+ const hasRenderProp = typeof children === "function";
29282
+ const idField = dndItemIdField ?? "id";
29283
+ const selected = selectedIds ? new Set(selectedIds) : localSelected;
29284
+ const emitSelection = (next) => {
29285
+ if (!selectedIds) setLocalSelected(next);
29286
+ if (selectEvent) {
29287
+ const payload = { selectedIds: Array.from(next) };
29288
+ eventBus.emit(`UI:${selectEvent}`, payload);
29289
+ }
29290
+ };
29291
+ const allSelected = selectable && data.length > 0 && data.every((r, i) => selected.has(String(r[idField] ?? i)));
29292
+ const toggleAll = () => {
29293
+ if (allSelected) emitSelection(/* @__PURE__ */ new Set());
29294
+ else emitSelection(new Set(data.map((r, i) => String(r[idField] ?? i))));
29295
+ };
29296
+ const toggleRow = (id) => {
29297
+ const next = new Set(selected);
29298
+ if (next.has(id)) next.delete(id);
29299
+ else next.add(id);
29300
+ emitSelection(next);
29301
+ };
29302
+ const handleSort = (col) => {
29303
+ if (!col.sortable || !sortEvent) return;
29304
+ const dir = sortColumn === (col.field ?? col.key) && sortDirection === "asc" ? "desc" : "asc";
29305
+ eventBus.emit(`UI:${sortEvent}`, { column: col.field ?? col.key, direction: dir });
29306
+ };
29307
+ const handleActionClick = (action, row) => (e) => {
29308
+ e.stopPropagation();
29309
+ const payload = {
29310
+ id: row.id,
29311
+ row
29312
+ };
29313
+ eventBus.emit(`UI:${action.event}`, payload);
29314
+ };
29315
+ if (isLoading) {
29316
+ return /* @__PURE__ */ jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: t("loading.items") || "Loading\u2026" }) });
29317
+ }
29318
+ if (error) {
29319
+ return /* @__PURE__ */ jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "error", children: error.message }) });
29320
+ }
29321
+ if (data.length === 0) {
29322
+ const emptyNode = /* @__PURE__ */ jsx(Box, { className: "text-center py-12", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: emptyMessage || t("empty.noItems") || "No records" }) });
29323
+ return dnd.enabled ? /* @__PURE__ */ jsx(Fragment, { children: dnd.wrapContainer(emptyNode) }) : emptyNode;
29324
+ }
29325
+ const lk = LOOKS[look];
29326
+ const header = /* @__PURE__ */ jsxs(
29327
+ Box,
29328
+ {
29329
+ role: "row",
29330
+ className: cn(
29331
+ "flex items-center gap-3 sticky top-0 z-10",
29332
+ "bg-[var(--color-surface-subtle)] border-b border-[var(--color-table-border)]",
29333
+ "text-[var(--color-text-muted)] uppercase text-xs font-semibold tracking-wide",
29334
+ lk.headPad
29335
+ ),
29336
+ children: [
29337
+ selectable && /* @__PURE__ */ jsx(Box, { className: "w-8 flex-shrink-0 flex items-center", children: /* @__PURE__ */ jsx(Checkbox, { checked: allSelected, onChange: toggleAll, "aria-label": "Select all rows" }) }),
29338
+ colDefs.map((col) => {
29339
+ const active = sortColumn === (col.field ?? col.key);
29340
+ return /* @__PURE__ */ jsxs(
29341
+ Box,
29342
+ {
29343
+ role: "columnheader",
29344
+ onClick: () => handleSort(col),
29345
+ className: cn(
29346
+ "flex items-center gap-1 min-w-0",
29347
+ col.width ?? "flex-1",
29348
+ alignClass[col.align ?? "left"],
29349
+ col.sortable && sortEvent && "cursor-pointer select-none hover:text-foreground"
29350
+ ),
29351
+ children: [
29352
+ col.icon && /* @__PURE__ */ jsx(Icon, { name: col.icon, size: "xs" }),
29353
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: columnLabel(col) }),
29354
+ col.sortable && sortEvent && /* @__PURE__ */ jsx(
29355
+ Icon,
29356
+ {
29357
+ name: active ? sortDirection === "asc" ? "chevron-up" : "chevron-down" : "chevrons-up-down",
29358
+ size: "xs",
29359
+ className: cn("flex-shrink-0", active ? "text-foreground" : "opacity-40")
29360
+ }
29361
+ )
29362
+ ]
29363
+ },
29364
+ col.key
29365
+ );
29366
+ }),
29367
+ itemActions && itemActions.length > 0 && /* @__PURE__ */ jsx(Box, { className: "w-px flex-shrink-0", "aria-hidden": true })
29368
+ ]
29369
+ }
29370
+ );
29371
+ const renderRow = (row, index) => {
29372
+ const id = String(row[idField] ?? index);
29373
+ const rowInner = /* @__PURE__ */ jsxs(
29374
+ Box,
29375
+ {
29376
+ role: "row",
29377
+ "data-entity-row": true,
29378
+ "data-entity-id": id,
29379
+ className: cn(
29380
+ "group flex items-center gap-3 transition-colors duration-fast",
29381
+ lk.rowPad,
29382
+ lk.divider && "border-b border-[var(--color-table-border)]",
29383
+ lk.striped && index % 2 === 1 && "bg-[var(--color-surface-subtle)]",
29384
+ "hover:bg-[var(--color-surface-subtle)]",
29385
+ look === "bordered" && "[&>*]:border-r [&>*]:border-[var(--color-table-border)] [&>*:last-child]:border-r-0"
29386
+ ),
29387
+ children: [
29388
+ selectable && /* @__PURE__ */ jsx(Box, { className: "w-8 flex-shrink-0 flex items-center", children: /* @__PURE__ */ jsx(
29389
+ Checkbox,
29390
+ {
29391
+ checked: selected.has(id),
29392
+ onChange: () => toggleRow(id),
29393
+ "aria-label": `Select row ${id}`
29394
+ }
29395
+ ) }),
29396
+ hasRenderProp ? /* @__PURE__ */ jsx(Box, { className: "flex-1 min-w-0", children: children(row, index) }) : colDefs.map((col) => {
29397
+ const raw = getNestedValue(row, col.field ?? col.key);
29398
+ const cellBase = cn(
29399
+ "flex items-center min-w-0",
29400
+ col.width ?? "flex-1",
29401
+ alignClass[col.align ?? "left"],
29402
+ weightClass[col.weight ?? "normal"],
29403
+ col.className
29404
+ );
29405
+ if (col.format === "badge" && raw != null && raw !== "") {
29406
+ return /* @__PURE__ */ jsx(Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsx(Badge, { variant: statusVariant4(String(raw)), size: "sm", children: String(raw) }) }, col.key);
29407
+ }
29408
+ return /* @__PURE__ */ jsx(Box, { role: "cell", className: cn(cellBase, "truncate block"), children: formatCell(raw, col.format) }, col.key);
29409
+ }),
29410
+ itemActions && itemActions.length > 0 && /* @__PURE__ */ jsx(HStack, { gap: "xs", className: "flex-shrink-0 opacity-60 group-hover:opacity-100 transition-opacity", children: itemActions.map((action, i) => /* @__PURE__ */ jsxs(
29411
+ Button,
29412
+ {
29413
+ variant: action.variant ?? "ghost",
29414
+ size: "sm",
29415
+ onClick: handleActionClick(action, row),
29416
+ "data-testid": `action-${action.event}`,
29417
+ "data-row-id": String(row.id),
29418
+ className: cn(action.variant === "danger" && "text-error hover:bg-error/10"),
29419
+ children: [
29420
+ action.icon && /* @__PURE__ */ jsx(Icon, { name: action.icon, size: "xs", className: "mr-1" }),
29421
+ action.label
29422
+ ]
29423
+ },
29424
+ i
29425
+ )) })
29426
+ ]
29427
+ }
29428
+ );
29429
+ return dnd.isZone ? /* @__PURE__ */ jsx(dnd.SortableItem, { id: row[idField] ?? id, children: rowInner }, id) : /* @__PURE__ */ jsx(React86__default.Fragment, { children: rowInner }, id);
29430
+ };
29431
+ const items = data.map((row) => row);
29432
+ const groups = groupBy ? groupData2(items, groupBy) : [{ label: "", items }];
29433
+ let runningIndex = 0;
29434
+ const body = /* @__PURE__ */ jsx(Box, { role: "rowgroup", children: groups.map((group, gi) => /* @__PURE__ */ jsxs(React86__default.Fragment, { children: [
29435
+ group.label && /* @__PURE__ */ jsx(Divider, { label: group.label, className: gi > 0 ? "mt-3" : "mt-0" }),
29436
+ group.items.map((row) => renderRow(row, runningIndex++))
29437
+ ] }, gi)) });
29438
+ return /* @__PURE__ */ jsxs(
29439
+ Box,
29440
+ {
29441
+ role: "table",
29442
+ className: cn("w-full text-sm", className),
29443
+ children: [
29444
+ header,
29445
+ dnd.wrapContainer(body),
29446
+ hasMore && /* @__PURE__ */ jsx(Box, { className: "flex justify-center py-3", children: /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "sm", onClick: () => setVisibleCount((p2) => p2 + (pageSize || 5)), children: [
29447
+ /* @__PURE__ */ jsx(Icon, { name: "chevron-down", size: "xs", className: "mr-1" }),
29448
+ t("common.showMore"),
29449
+ " (",
29450
+ ordered.length - visibleCount,
29451
+ " remaining)"
29452
+ ] }) })
29453
+ ]
29454
+ }
29455
+ );
29456
+ }
29457
+ var alignClass, weightClass, LOOKS;
29458
+ var init_TableView = __esm({
29459
+ "components/molecules/TableView.tsx"() {
29460
+ "use client";
29461
+ init_cn();
29462
+ init_getNestedValue();
29463
+ init_useEventBus();
29464
+ init_useTranslate();
29465
+ init_Box();
29466
+ init_Stack();
29467
+ init_Typography();
29468
+ init_Badge();
29469
+ init_Button();
29470
+ init_Icon();
29471
+ init_Checkbox();
29472
+ init_Divider();
29473
+ init_useDataDnd();
29474
+ createLogger("almadar:ui:table-view");
29475
+ alignClass = {
29476
+ left: "justify-start text-left",
29477
+ center: "justify-center text-center",
29478
+ right: "justify-end text-right"
29479
+ };
29480
+ weightClass = {
29481
+ normal: "",
29482
+ medium: "font-medium",
29483
+ semibold: "font-semibold"
29484
+ };
29485
+ LOOKS = {
29486
+ dense: { rowPad: "px-3 py-2", headPad: "px-3 py-2", striped: false, divider: true },
29487
+ spacious: { rowPad: "px-5 py-4", headPad: "px-5 py-3", striped: false, divider: true },
29488
+ striped: { rowPad: "px-3 py-2", headPad: "px-3 py-2", striped: true, divider: false },
29489
+ borderless: { rowPad: "px-3 py-2", headPad: "px-3 py-2", striped: false, divider: false },
29490
+ bordered: { rowPad: "px-3 py-2", headPad: "px-3 py-2", striped: false, divider: true }
29491
+ };
29492
+ TableView.displayName = "TableView";
29493
+ }
29494
+ });
29190
29495
  function formatNumber(value, format) {
29191
29496
  if (value == null) return "0";
29192
29497
  const v = typeof value === "number" ? value : value;
@@ -46244,6 +46549,7 @@ var init_component_registry_generated = __esm({
46244
46549
  init_Switch();
46245
46550
  init_TabbedContainer();
46246
46551
  init_Table();
46552
+ init_TableView();
46247
46553
  init_Tabs();
46248
46554
  init_TagCloud();
46249
46555
  init_TagInput();
@@ -46561,6 +46867,7 @@ var init_component_registry_generated = __esm({
46561
46867
  "Switch": Switch,
46562
46868
  "TabbedContainer": TabbedContainer,
46563
46869
  "Table": Table,
46870
+ "TableView": TableView,
46564
46871
  "Tabs": Tabs,
46565
46872
  "TagCloud": TagCloud,
46566
46873
  "TagInput": TagInput,