@almadar/ui 6.14.0 → 6.16.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.
@@ -2117,7 +2117,7 @@ var init_Textarea = __esm({
2117
2117
  init_cn();
2118
2118
  init_useEventBus();
2119
2119
  exports.Textarea = React79__namespace.default.forwardRef(
2120
- ({ className, error, onChange, ...props }, ref) => {
2120
+ ({ className, error, onChange, action, onKeyDown, ...props }, ref) => {
2121
2121
  const eventBus = useEventBus();
2122
2122
  const handleChange = (e) => {
2123
2123
  if (typeof onChange === "string") {
@@ -2126,11 +2126,18 @@ var init_Textarea = __esm({
2126
2126
  onChange?.(e);
2127
2127
  }
2128
2128
  };
2129
+ const handleKeyDown = (e) => {
2130
+ onKeyDown?.(e);
2131
+ if (!action || e.key !== "Enter" || !(e.metaKey || e.ctrlKey)) return;
2132
+ e.preventDefault();
2133
+ eventBus.emit(`UI:${action}`, { value: e.currentTarget.value });
2134
+ };
2129
2135
  return /* @__PURE__ */ jsxRuntime.jsx(
2130
2136
  "textarea",
2131
2137
  {
2132
2138
  ref,
2133
2139
  onChange: handleChange,
2140
+ onKeyDown: handleKeyDown,
2134
2141
  className: cn(
2135
2142
  "block w-full border-[length:var(--border-width)] shadow-sm",
2136
2143
  "px-3 py-2 text-sm text-foreground",
@@ -20056,6 +20063,29 @@ var init_useCanvasGestures = __esm({
20056
20063
  }
20057
20064
  });
20058
20065
 
20066
+ // lib/keyMapEvent.ts
20067
+ function isEditableTarget(target) {
20068
+ if (!(target instanceof HTMLElement)) return false;
20069
+ const tag = target.tagName;
20070
+ if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
20071
+ return target.isContentEditable || target.contentEditable === "true";
20072
+ }
20073
+ function keyMapCode(e) {
20074
+ let code = e.code;
20075
+ if (e.altKey) code = `Alt+${code}`;
20076
+ if (e.shiftKey) code = `Shift+${code}`;
20077
+ if (e.metaKey || e.ctrlKey) code = `Mod+${code}`;
20078
+ return code;
20079
+ }
20080
+ function resolveKeyMapEvent(map, e) {
20081
+ if (!map || isEditableTarget(e.target)) return void 0;
20082
+ return map[keyMapCode(e)] ?? map[e.code];
20083
+ }
20084
+ var init_keyMapEvent = __esm({
20085
+ "lib/keyMapEvent.ts"() {
20086
+ }
20087
+ });
20088
+
20059
20089
  // lib/isometric.ts
20060
20090
  function isoToScreen(tileX, tileY, cellWidth, baseOffsetX, layout = "isometric") {
20061
20091
  const w = cellWidth;
@@ -20774,14 +20804,14 @@ function Canvas2D({
20774
20804
  React79.useEffect(() => {
20775
20805
  if (!keyMap && !keyUpMap) return;
20776
20806
  const onDown = (e) => {
20777
- const ev = keyMap?.[e.code];
20807
+ const ev = resolveKeyMapEvent(keyMap, e);
20778
20808
  if (ev) {
20779
20809
  eventBus.emit(`UI:${ev}`, {});
20780
20810
  e.preventDefault();
20781
20811
  }
20782
20812
  };
20783
20813
  const onUp = (e) => {
20784
- const ev = keyUpMap?.[e.code];
20814
+ const ev = resolveKeyMapEvent(keyUpMap, e);
20785
20815
  if (ev) eventBus.emit(`UI:${ev}`, {});
20786
20816
  };
20787
20817
  window.addEventListener("keydown", onDown);
@@ -20905,6 +20935,7 @@ var init_Canvas2D = __esm({
20905
20935
  init_useCamera();
20906
20936
  init_useCanvasGestures();
20907
20937
  init_verificationRegistry();
20938
+ init_keyMapEvent();
20908
20939
  init_webPainter2d();
20909
20940
  init_projector();
20910
20941
  init_paintDispatch();
@@ -25137,6 +25168,40 @@ var init_DashboardLayout = __esm({
25137
25168
  NavLinkBottom.displayName = "NavLinkBottom";
25138
25169
  }
25139
25170
  });
25171
+
25172
+ // lib/relationLabel.ts
25173
+ function relationLabel(value) {
25174
+ if (value === null || typeof value !== "object" || Array.isArray(value) || value instanceof Date)
25175
+ return null;
25176
+ for (const key of ["name", "title", "label"]) {
25177
+ const candidate = value[key];
25178
+ if (typeof candidate === "string" && candidate !== "") return candidate;
25179
+ }
25180
+ const id = value.id;
25181
+ return id !== void 0 && id !== null ? String(id) : null;
25182
+ }
25183
+ function relationDisplayLabels(value, options) {
25184
+ if (value === void 0 || value === null || value === "") return [];
25185
+ if (Array.isArray(value)) {
25186
+ return value.flatMap((item) => relationDisplayLabels(item, options));
25187
+ }
25188
+ const hydrated = relationLabel(value);
25189
+ if (hydrated !== null) return [hydrated];
25190
+ const raw = String(value);
25191
+ const match = options?.find((opt) => opt.value === raw);
25192
+ return [match ? match.label : raw];
25193
+ }
25194
+ function resolveRelationCellDisplay(value, options) {
25195
+ if (value === null || value === void 0 || value === "") return void 0;
25196
+ const isObjectShaped = typeof value === "object" && !(value instanceof Date) || Array.isArray(value) && value.some((v) => v !== null && typeof v === "object" && !(v instanceof Date));
25197
+ if (!isObjectShaped && !options) return void 0;
25198
+ const labels = relationDisplayLabels(value, options);
25199
+ return labels.length > 0 ? labels.join(", ") : void 0;
25200
+ }
25201
+ var init_relationLabel = __esm({
25202
+ "lib/relationLabel.ts"() {
25203
+ }
25204
+ });
25140
25205
  function downloadItemUrl(url, label) {
25141
25206
  const a = document.createElement("a");
25142
25207
  a.href = url;
@@ -25968,7 +26033,8 @@ function DataGrid({
25968
26033
  positionEvent,
25969
26034
  dndItemIdField,
25970
26035
  dndRoot,
25971
- look = "dense"
26036
+ look = "dense",
26037
+ relationsData
25972
26038
  }) {
25973
26039
  const eventBus = useEventBus();
25974
26040
  const { t } = hooks.useTranslate();
@@ -26158,6 +26224,10 @@ function DataGrid({
26158
26224
  );
26159
26225
  }
26160
26226
  const titleValue = core.getNestedValue(itemData, titleField?.name ?? "");
26227
+ const titleDisplay = resolveRelationCellDisplay(
26228
+ titleValue,
26229
+ titleField ? relationsData?.[titleField.name] : void 0
26230
+ ) ?? (titleValue !== void 0 && titleValue !== null ? String(titleValue) : void 0);
26161
26231
  return wrapDnd(
26162
26232
  /* @__PURE__ */ jsxRuntime.jsxs(
26163
26233
  exports.Box,
@@ -26182,7 +26252,7 @@ function DataGrid({
26182
26252
  "img",
26183
26253
  {
26184
26254
  src: imgUrl,
26185
- alt: titleValue !== void 0 ? String(titleValue) : "",
26255
+ alt: titleDisplay ?? "",
26186
26256
  className: "w-full h-full object-cover",
26187
26257
  loading: "lazy"
26188
26258
  }
@@ -26197,18 +26267,18 @@ function DataGrid({
26197
26267
  onChange: () => toggleSelection(id),
26198
26268
  onClick: (e) => e.stopPropagation(),
26199
26269
  className: "w-4 h-4 mt-1 flex-shrink-0 accent-primary",
26200
- "aria-label": t("card.selectItem", { item: titleValue !== void 0 ? String(titleValue) : t("card.itemFallback") })
26270
+ "aria-label": t("card.selectItem", { item: titleDisplay ?? t("card.itemFallback") })
26201
26271
  }
26202
26272
  ),
26203
26273
  /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "xs", className: "flex-1 min-w-0", children: [
26204
- titleValue !== void 0 && titleValue !== null && /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "xs", className: "items-center min-w-0", children: [
26274
+ titleDisplay !== void 0 && /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "xs", className: "items-center min-w-0", children: [
26205
26275
  titleField?.icon && renderIconInput(titleField.icon, { size: "sm", className: "text-primary flex-shrink-0" }),
26206
26276
  /* @__PURE__ */ jsxRuntime.jsx(
26207
26277
  exports.Typography,
26208
26278
  {
26209
26279
  variant: titleField?.variant === "h3" ? "h3" : "h4",
26210
26280
  className: "font-semibold truncate min-w-0",
26211
- children: String(titleValue)
26281
+ children: titleDisplay
26212
26282
  }
26213
26283
  )
26214
26284
  ] }),
@@ -26217,7 +26287,7 @@ function DataGrid({
26217
26287
  if (val === void 0 || val === null || val === "") return null;
26218
26288
  return /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "xs", className: "items-center", children: [
26219
26289
  field.icon && renderIconInput(field.icon, { size: "xs" }),
26220
- /* @__PURE__ */ jsxRuntime.jsx(exports.Badge, { variant: resolveBadgeVariant(field, String(val)), children: humanizeEnumValue(formatValue(val, field.format)) })
26290
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Badge, { variant: resolveBadgeVariant(field, String(val)), children: resolveRelationCellDisplay(val, relationsData?.[field.name]) ?? humanizeEnumValue(formatValue(val, field.format)) })
26221
26291
  ] }, field.name);
26222
26292
  }) })
26223
26293
  ] }),
@@ -26256,7 +26326,7 @@ function DataGrid({
26256
26326
  bodyFields.filter((f3) => f3.variant === "caption" && f3.format !== "boolean").map((field) => {
26257
26327
  const value = core.getNestedValue(itemData, field.name);
26258
26328
  if (value === void 0 || value === null || value === "") return null;
26259
- return /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", color: "secondary", className: "line-clamp-2", children: formatValue(value, field.format) }, field.name);
26329
+ return /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", color: "secondary", className: "line-clamp-2", children: resolveRelationCellDisplay(value, relationsData?.[field.name]) ?? formatValue(value, field.format) }, field.name);
26260
26330
  }),
26261
26331
  /* @__PURE__ */ jsxRuntime.jsx(exports.HStack, { gap: "md", className: "flex-wrap gap-y-1", children: bodyFields.filter((f3) => f3.variant !== "caption" || f3.format === "boolean").map((field) => {
26262
26332
  const value = core.getNestedValue(itemData, field.name);
@@ -26271,7 +26341,7 @@ function DataGrid({
26271
26341
  return /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "xs", className: "items-center", children: [
26272
26342
  field.icon && renderIconInput(field.icon, { size: "xs", className: "text-muted-foreground" }),
26273
26343
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", color: "secondary", className: "sr-only", children: (field.label ?? fieldLabel2(field.name)) + ":" }),
26274
- /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", color: "secondary", children: formatValue(value, field.format) })
26344
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", color: "secondary", children: resolveRelationCellDisplay(value, relationsData?.[field.name]) ?? formatValue(value, field.format) })
26275
26345
  ] }, field.name);
26276
26346
  }) })
26277
26347
  ] }) })
@@ -26315,6 +26385,7 @@ var init_DataGrid = __esm({
26315
26385
  "use client";
26316
26386
  init_cn();
26317
26387
  init_format();
26388
+ init_relationLabel();
26318
26389
  init_getNestedValue();
26319
26390
  init_useEventBus();
26320
26391
  init_Box();
@@ -26367,7 +26438,9 @@ function statusVariant3(value) {
26367
26438
  if (["new", "created", "scheduled", "queued", "info"].includes(v)) return "info";
26368
26439
  return "default";
26369
26440
  }
26370
- function formatValue2(value, format, boolLabels) {
26441
+ function formatValue2(value, format, boolLabels, relationOptions) {
26442
+ const relationDisplay = resolveRelationCellDisplay(value, relationOptions);
26443
+ if (relationDisplay !== void 0) return relationDisplay;
26371
26444
  if (value !== void 0 && value !== null && (format === "boolean" || typeof value === "boolean")) {
26372
26445
  const isNo = value === false || value === 0 || String(value) === "false";
26373
26446
  return isNo ? boolLabels?.no ?? "No" : boolLabels?.yes ?? "Yes";
@@ -26427,7 +26500,8 @@ function DataList({
26427
26500
  positionEvent,
26428
26501
  dndItemIdField,
26429
26502
  dndRoot,
26430
- look = "dense"
26503
+ look = "dense",
26504
+ relationsData
26431
26505
  }) {
26432
26506
  const eventBus = useEventBus();
26433
26507
  const { t } = hooks.useTranslate();
@@ -26615,13 +26689,13 @@ function DataList({
26615
26689
  return f3.variant === "badge" ? (
26616
26690
  // `format` applies here too — a boolean field badged
26617
26691
  // without it renders the raw "false" instead of "No".
26618
- /* @__PURE__ */ jsxRuntime.jsx(exports.Badge, { variant: statusVariant3(String(v)), children: formatValue2(v, f3.format) }, f3.name)
26692
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Badge, { variant: statusVariant3(String(v)), children: formatValue2(v, f3.format, void 0, relationsData?.[f3.name]) }, f3.name)
26619
26693
  ) : /* @__PURE__ */ jsxRuntime.jsx(
26620
26694
  exports.Typography,
26621
26695
  {
26622
26696
  variant: "caption",
26623
26697
  className: cn("text-xs", isSent ? "opacity-70" : "text-muted-foreground"),
26624
- children: formatValue2(v, f3.format)
26698
+ children: formatValue2(v, f3.format, void 0, relationsData?.[f3.name])
26625
26699
  },
26626
26700
  f3.name
26627
26701
  );
@@ -26699,6 +26773,10 @@ function DataList({
26699
26773
  }
26700
26774
  const id = itemData.id || String(index);
26701
26775
  const titleValue = core.getNestedValue(itemData, titleField?.name ?? "");
26776
+ const titleDisplay = resolveRelationCellDisplay(
26777
+ titleValue,
26778
+ titleField ? relationsData?.[titleField.name] : void 0
26779
+ ) ?? (titleValue !== void 0 && titleValue !== null ? String(titleValue) : void 0);
26702
26780
  return wrapDnd(
26703
26781
  /* @__PURE__ */ jsxRuntime.jsxs(exports.Box, { "data-entity-row": true, "data-entity-id": id, onClick: rowClickEvent ? handleRowClick(itemData) : void 0, className: cn(rowClickEvent && "cursor-pointer"), children: [
26704
26782
  /* @__PURE__ */ jsxRuntime.jsxs(
@@ -26723,7 +26801,7 @@ function DataList({
26723
26801
  {
26724
26802
  variant: titleField?.variant === "h3" ? "h3" : "h4",
26725
26803
  className: cn("font-semibold truncate flex-1", isCompact && "text-sm"),
26726
- children: String(titleValue)
26804
+ children: titleDisplay
26727
26805
  }
26728
26806
  ),
26729
26807
  badgeFields.map((field) => {
@@ -26731,7 +26809,7 @@ function DataList({
26731
26809
  if (val === void 0 || val === null) return null;
26732
26810
  return /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "xs", className: "items-center flex-shrink-0", children: [
26733
26811
  field.icon && renderIconInput2(field.icon, { size: "xs" }),
26734
- /* @__PURE__ */ jsxRuntime.jsx(exports.Badge, { variant: statusVariant3(String(val)), children: formatValue2(val, field.format) })
26812
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Badge, { variant: statusVariant3(String(val)), children: formatValue2(val, field.format, void 0, relationsData?.[field.name]) })
26735
26813
  ] }, field.name);
26736
26814
  })
26737
26815
  ] }),
@@ -26752,7 +26830,7 @@ function DataList({
26752
26830
  ]
26753
26831
  }
26754
26832
  ),
26755
- /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", color: "secondary", children: formatValue2(value, field.format, { yes: t("common.yes"), no: t("common.no") }) })
26833
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", color: "secondary", children: formatValue2(value, field.format, { yes: t("common.yes"), no: t("common.no") }, relationsData?.[field.name]) })
26756
26834
  ] }, field.name);
26757
26835
  }) }),
26758
26836
  progressFields.map((field) => {
@@ -26832,6 +26910,7 @@ var init_DataList = __esm({
26832
26910
  "use client";
26833
26911
  init_cn();
26834
26912
  init_format();
26913
+ init_relationLabel();
26835
26914
  init_getNestedValue();
26836
26915
  init_useEventBus();
26837
26916
  init_Box();
@@ -34094,6 +34173,7 @@ var init_MathCanvas = __esm({
34094
34173
  "use client";
34095
34174
  init_useEventBus();
34096
34175
  init_perf();
34176
+ init_keyMapEvent();
34097
34177
  init_atoms();
34098
34178
  init_Stack();
34099
34179
  init_gameFonts();
@@ -34147,14 +34227,14 @@ var init_MathCanvas = __esm({
34147
34227
  React79.useEffect(() => {
34148
34228
  if (!stableKeyMap && !stableKeyUpMap) return;
34149
34229
  const onDown = (e) => {
34150
- const ev = stableKeyMap?.[e.code];
34230
+ const ev = resolveKeyMapEvent(stableKeyMap, e);
34151
34231
  if (ev) {
34152
34232
  eventBus.emit(`UI:${ev}`, {});
34153
34233
  e.preventDefault();
34154
34234
  }
34155
34235
  };
34156
34236
  const onUp = (e) => {
34157
- const ev = stableKeyUpMap?.[e.code];
34237
+ const ev = resolveKeyMapEvent(stableKeyUpMap, e);
34158
34238
  if (ev) eventBus.emit(`UI:${ev}`, {});
34159
34239
  };
34160
34240
  window.addEventListener("keydown", onDown);
@@ -36570,33 +36650,6 @@ var init_Lightbox = __esm({
36570
36650
  exports.Lightbox.displayName = "Lightbox";
36571
36651
  }
36572
36652
  });
36573
-
36574
- // lib/relationLabel.ts
36575
- function relationLabel(value) {
36576
- if (value === null || typeof value !== "object" || Array.isArray(value) || value instanceof Date)
36577
- return null;
36578
- for (const key of ["name", "title", "label"]) {
36579
- const candidate = value[key];
36580
- if (typeof candidate === "string" && candidate !== "") return candidate;
36581
- }
36582
- const id = value.id;
36583
- return id !== void 0 && id !== null ? String(id) : null;
36584
- }
36585
- function relationDisplayLabels(value, options) {
36586
- if (value === void 0 || value === null || value === "") return [];
36587
- if (Array.isArray(value)) {
36588
- return value.flatMap((item) => relationDisplayLabels(item, options));
36589
- }
36590
- const hydrated = relationLabel(value);
36591
- if (hydrated !== null) return [hydrated];
36592
- const raw = String(value);
36593
- const match = options?.find((opt) => opt.value === raw);
36594
- return [match ? match.label : raw];
36595
- }
36596
- var init_relationLabel = __esm({
36597
- "lib/relationLabel.ts"() {
36598
- }
36599
- });
36600
36653
  function renderIconInput3(icon, props) {
36601
36654
  return typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: icon, ...props }) : /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon, ...props });
36602
36655
  }
@@ -36660,7 +36713,8 @@ function TableView({
36660
36713
  reorderEvent,
36661
36714
  positionEvent,
36662
36715
  dndItemIdField,
36663
- dndRoot
36716
+ dndRoot,
36717
+ relationsData
36664
36718
  }) {
36665
36719
  const eventBus = useEventBus();
36666
36720
  const { t } = hooks.useTranslate();
@@ -36745,7 +36799,7 @@ function TableView({
36745
36799
  const colFloors = React79__namespace.default.useMemo(
36746
36800
  () => colDefs.map((col) => {
36747
36801
  const longest = data.reduce((widest, row) => {
36748
- const cell = formatCell(asFieldValue(core.getNestedValue(row, col.field ?? col.key)), col.format);
36802
+ const cell = formatCell(asFieldValue(core.getNestedValue(row, col.field ?? col.key)), col.format, relationsData?.[col.field ?? col.key]);
36749
36803
  return Math.max(widest, cell.length);
36750
36804
  }, columnLabel(col).length);
36751
36805
  const chrome = col.format === "badge" ? BADGE_CHROME_CH : 0;
@@ -36845,9 +36899,11 @@ function TableView({
36845
36899
  col.className
36846
36900
  );
36847
36901
  if (col.format === "badge" && raw != null && raw !== "") {
36848
- return /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsxRuntime.jsx(exports.Badge, { variant: statusVariant4(String(raw)), size: "sm", className: "whitespace-nowrap", children: humanizeEnumValue(String(raw)) }) }, col.key);
36902
+ const relationDisplay = resolveRelationCellDisplay(raw, relationsData?.[col.field ?? col.key]);
36903
+ const label = relationDisplay ?? humanizeEnumValue(String(raw));
36904
+ return /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsxRuntime.jsx(exports.Badge, { variant: statusVariant4(String(raw)), size: "sm", className: "whitespace-nowrap", children: label }) }, col.key);
36849
36905
  }
36850
- return /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-foreground", children: formatCell(raw, col.format) }) }, col.key);
36906
+ return /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-foreground", children: formatCell(raw, col.format, relationsData?.[col.field ?? col.key]) }) }, col.key);
36851
36907
  }),
36852
36908
  hasActions && /* @__PURE__ */ jsxRuntime.jsxs(
36853
36909
  exports.HStack,
@@ -36949,11 +37005,9 @@ var init_TableView = __esm({
36949
37005
  init_Menu();
36950
37006
  init_useDataDnd();
36951
37007
  tableViewLog = logger.createLogger("almadar:ui:table-view");
36952
- formatCell = (value, format) => {
36953
- if (value !== null && value !== void 0 && typeof value === "object" && !(value instanceof Date)) {
36954
- const labels = relationDisplayLabels(value);
36955
- if (labels.length > 0) return labels.join(", ");
36956
- }
37008
+ formatCell = (value, format, relationOptions) => {
37009
+ const relationDisplay = resolveRelationCellDisplay(value, relationOptions);
37010
+ if (relationDisplay !== void 0) return relationDisplay;
36957
37011
  return formatValue(value, format);
36958
37012
  };
36959
37013
  MAX_MEASURED_COL_CH = 32;
@@ -45892,7 +45946,8 @@ function DataTable({
45892
45946
  headerActions,
45893
45947
  showTotal = true,
45894
45948
  className,
45895
- look = "dense"
45949
+ look = "dense",
45950
+ relationsData
45896
45951
  }) {
45897
45952
  const [openActionMenu, setOpenActionMenu] = React79.useState(
45898
45953
  null
@@ -46187,6 +46242,11 @@ function DataTable({
46187
46242
  "data-column": String(col.key),
46188
46243
  className: "px-4 py-3 text-sm text-foreground whitespace-nowrap sm:whitespace-normal",
46189
46244
  children: col.render ? col.render(cellValue, row, rowIndex) : (() => {
46245
+ const relationDisplay = resolveRelationCellDisplay(
46246
+ cellValue,
46247
+ relationsData?.[String(col.key)]
46248
+ );
46249
+ if (relationDisplay !== void 0) return relationDisplay;
46190
46250
  const boolVal = asBooleanValue2(cellValue);
46191
46251
  if (boolVal !== null) {
46192
46252
  return boolVal ? /* @__PURE__ */ jsxRuntime.jsx(exports.Badge, { variant: "success", children: t("common.yes") }) : /* @__PURE__ */ jsxRuntime.jsx(exports.Badge, { variant: "neutral", children: t("common.no") });
@@ -46275,6 +46335,7 @@ var init_DataTable = __esm({
46275
46335
  init_cn();
46276
46336
  init_format();
46277
46337
  init_getNestedValue();
46338
+ init_relationLabel();
46278
46339
  init_atoms();
46279
46340
  init_Box();
46280
46341
  init_Stack();
@@ -6,8 +6,8 @@ import { LucideIcon } from 'lucide-react';
6
6
  import { C as ColorToken, U as UiError, D as DrawableNode, P as Point, a as Projector, F as FxOverlayItem, M as MeshShapeKind, b as MeshMaterial, c as DrawGroupProps, d as DrawTextProps, e as DrawMeshProps, L as LinkAction, I as ImageSource } from '../paintDispatch-Dh1pgljl.cjs';
7
7
  export { f as FxOverlayKind, R as Rect } from '../paintDispatch-Dh1pgljl.cjs';
8
8
  import { SExpr } from '@almadar/evaluator';
9
- import { U as UnitAnimationState, F as FacingDirection, S as SpriteSheetUrls, g as SpriteFrameDims, R as ResolvedFrame, h as CanvasLighting, i as CanvasPost, a as IsometricUnit, j as CameraState, k as FieldInfo, l as OrbitalTraitInfo, m as OrbitalPageInfo, T as TraitLevelData, E as ExternalLink } from '../avl-schema-parser-B25PL-a8.cjs';
10
- export { B as BoardTile, G as GameAction, n as GamePhase, o as GameState, p as GameUnit, b as IsometricFeature, I as IsometricTile, P as Position, q as UnitTrait, r as calculateAttackTargets, s as calculateValidMoves, t as createInitialGameState } from '../avl-schema-parser-B25PL-a8.cjs';
9
+ import { U as UnitAnimationState, F as FacingDirection, S as SpriteSheetUrls, g as SpriteFrameDims, R as ResolvedFrame, h as CanvasLighting, i as CanvasPost, a as IsometricUnit, j as CameraState, k as FieldInfo, l as OrbitalTraitInfo, m as OrbitalPageInfo, T as TraitLevelData, E as ExternalLink } from '../avl-schema-parser-dGHC5jUf.cjs';
10
+ export { B as BoardTile, G as GameAction, n as GamePhase, o as GameState, p as GameUnit, b as IsometricFeature, I as IsometricTile, P as Position, q as UnitTrait, r as calculateAttackTargets, s as calculateValidMoves, t as createInitialGameState } from '../avl-schema-parser-dGHC5jUf.cjs';
11
11
  export { G as GameAudioContext, a as GameAudioContextValue, e as GameAudioControls, b as GameAudioProvider, c as GameAudioProviderProps, U as UseGameAudioOptions, f as useGameAudio, u as useGameAudioContext } from '../GameAudioProvider-Cpw_lMPY.cjs';
12
12
  import { i as EditorMotion, j as EditorOperator, C as ContentSegment, L as LessonSegment, F as InteractiveOrbitalType, c as DomLayoutData, e as DomStateNode, V as VisualizerConfig, f as DomTransitionLabel } from '../cn-Do5gsPBm.cjs';
13
13
  export { G as BloomLevel, H as BloomQuizBlock, J as BloomQuizBlockProps, M as MixedSegment, o as cn, x as parseLessonSegments, K as parseMarkdownWithCodeBlocks } from '../cn-Do5gsPBm.cjs';
@@ -372,7 +372,7 @@ interface TextareaProps extends Omit<React__default.TextareaHTMLAttributes<HTMLT
372
372
  placeholder?: string;
373
373
  /** Number of visible rows */
374
374
  rows?: number;
375
- /** Declarative event name for trait dispatch */
375
+ /** Declarative event: fires on ⌘/Ctrl+Enter with `{ value }`. */
376
376
  action?: EventKey;
377
377
  /** Error message */
378
378
  error?: string;
@@ -2307,9 +2307,9 @@ interface ControlGridProps {
2307
2307
  }>;
2308
2308
  /** Per-direction PRESS → the board's SEMANTIC event (kind="dpad"), e.g. `{ left: "LEFT", right: "RIGHT", up: "JUMP" }`.
2309
2309
  * Lets the d-pad emit the SAME intent events as the keyboard so the FSM stays device-agnostic. */
2310
- directionEvents?: Partial<Record<DPadDirection, string>>;
2310
+ directionEvents?: Partial<Record<DPadDirection, EventKey>>;
2311
2311
  /** Per-direction RELEASE → semantic event, e.g. `{ left: "STOP", right: "STOP" }`. Omit a direction to ignore its release. */
2312
- directionReleaseEvents?: Partial<Record<DPadDirection, string>>;
2312
+ directionReleaseEvents?: Partial<Record<DPadDirection, EventKey>>;
2313
2313
  /** Per-direction sprite assets for kind="dpad" buttons (e.g. Kenny arrow PNGs). Falls back to arrow emoji. */
2314
2314
  directionAssets?: Partial<Record<DPadDirection, Asset>>;
2315
2315
  size?: 'sm' | 'md' | 'lg';
@@ -2593,10 +2593,10 @@ interface Canvas2DProps {
2593
2593
  }>;
2594
2594
  /** Emits UI:{tileLeaveEvent} with {} on pointer leave. */
2595
2595
  tileLeaveEvent?: EventEmit<Record<string, never>>;
2596
- /** Maps a keydown `e.code` the board's SEMANTIC event (device-agnostic input). */
2597
- keyMap?: Record<string, string>;
2598
- /** Maps a keyup `e.code` the board's SEMANTIC event. */
2599
- keyUpMap?: Record<string, string>;
2596
+ /** Maps a keydown `e.code` optionally prefixed `Mod+` (⌘/Ctrl), `Shift+`, `Alt+` in that order — to the board's SEMANTIC event (device-agnostic input), emitted as `UI:{event}`; keystrokes inside inputs/textareas never route. */
2597
+ keyMap?: Record<string, EventKey>;
2598
+ /** Maps a keyup `e.code` optionally prefixed `Mod+` (⌘/Ctrl), `Shift+`, `Alt+` in that order — to the board's SEMANTIC event, emitted as `UI:{event}`; keystrokes inside inputs/textareas never route. */
2599
+ keyUpMap?: Record<string, EventKey>;
2600
2600
  /** Enter scene-edit mode: click selects/deselects a drawable, drag moves it. */
2601
2601
  editable?: boolean;
2602
2602
  /** The currently-selected drawable id (controlled); `null`/undefined = none. */
@@ -2754,8 +2754,10 @@ interface CanvasProps {
2754
2754
  type?: string;
2755
2755
  elevation?: number;
2756
2756
  }>;
2757
- keyMap?: Record<string, string>;
2758
- keyUpMap?: Record<string, string>;
2757
+ /** Maps a keydown `e.code` — optionally prefixed `Mod+` (⌘/Ctrl), `Shift+`, `Alt+` in that order — to the board's SEMANTIC event (device-agnostic input), emitted as `UI:{event}`; keystrokes inside inputs/textareas never route. */
2758
+ keyMap?: Record<string, EventKey>;
2759
+ /** Maps a keyup `e.code` — optionally prefixed `Mod+` (⌘/Ctrl), `Shift+`, `Alt+` in that order — to the board's SEMANTIC event, emitted as `UI:{event}`; keystrokes inside inputs/textareas never route. */
2760
+ keyUpMap?: Record<string, EventKey>;
2759
2761
  /** Enter scene-edit mode: click selects/deselects a drawable, drag moves it. */
2760
2762
  editable?: boolean;
2761
2763
  /** The currently-selected drawable id (controlled); `null`/undefined = none. */
@@ -6516,10 +6518,10 @@ interface MathCanvasProps {
6516
6518
  type?: string;
6517
6519
  index: number;
6518
6520
  }) => void;
6519
- /** Maps a keydown `e.code` the board's SEMANTIC event (device-agnostic input), emitted as `UI:{event}` — same contract as the game canvas keyMap. */
6520
- keyMap?: Record<string, string>;
6521
- /** Maps a keyup `e.code` the board's SEMANTIC event. */
6522
- keyUpMap?: Record<string, string>;
6521
+ /** Maps a keydown `e.code` optionally prefixed `Mod+` (⌘/Ctrl), `Shift+`, `Alt+` in that order — to the board's SEMANTIC event (device-agnostic input), emitted as `UI:{event}` — same contract as the game canvas keyMap; keystrokes inside inputs/textareas never route. */
6522
+ keyMap?: Record<string, EventKey>;
6523
+ /** Maps a keyup `e.code` optionally prefixed `Mod+` (⌘/Ctrl), `Shift+`, `Alt+` in that order — to the board's SEMANTIC event, emitted as `UI:{event}`; keystrokes inside inputs/textareas never route. */
6524
+ keyUpMap?: Record<string, EventKey>;
6523
6525
  isLoading?: boolean;
6524
6526
  error?: UiError | null;
6525
6527
  }
@@ -7869,6 +7871,7 @@ interface DataGridItemAction {
7869
7871
  * with sort, select, and drag-reorder.
7870
7872
  *
7871
7873
  * @capabilities admin table, records grid, user list, CRUD list, manage-records view, spreadsheet-style data grid, sortable columns
7874
+ * @fieldsContract display
7872
7875
  */
7873
7876
  interface DataGridProps extends DataDndProps {
7874
7877
  /**
@@ -7933,8 +7936,13 @@ interface DataGridProps extends DataDndProps {
7933
7936
  * data-grid / data-list / entity-table share one knob name from authors.
7934
7937
  */
7935
7938
  look?: "dense" | "spacious" | "striped" | "borderless" | "card-rows";
7939
+ /** Relation display data: { fieldName: [{value, label}] } — injected
7940
+ * server-side by the runtime (relation-option injection) or bound by
7941
+ * compiled codegen; resolves stored foreign ids to display names for a
7942
+ * field whose type is relation. Same contract DetailPanel takes. */
7943
+ relationsData?: Record<string, readonly RelationOption[]>;
7936
7944
  }
7937
- declare function DataGrid({ entity, fields, columns, itemActions, maxInlineActions, scrollX, cols, gap, minCardWidth, className, isLoading, error, imageField, selectable, selectionEvent, infiniteScroll, loadMoreEvent, hasMore, children, pageSize, renderItem: schemaRenderItem, dragGroup, accepts, sortable, dropEvent, reorderEvent, positionEvent, dndItemIdField, dndRoot, look, }: DataGridProps): string | number | bigint | boolean | React__default.JSX.Element | Iterable<React__default.ReactNode> | Promise<string | number | bigint | boolean | React__default.ReactPortal | React__default.ReactElement<unknown, string | React__default.JSXElementConstructor<any>> | Iterable<React__default.ReactNode> | null | undefined> | null | undefined;
7945
+ declare function DataGrid({ entity, fields, columns, itemActions, maxInlineActions, scrollX, cols, gap, minCardWidth, className, isLoading, error, imageField, selectable, selectionEvent, infiniteScroll, loadMoreEvent, hasMore, children, pageSize, renderItem: schemaRenderItem, dragGroup, accepts, sortable, dropEvent, reorderEvent, positionEvent, dndItemIdField, dndRoot, look, relationsData, }: DataGridProps): string | number | bigint | boolean | React__default.JSX.Element | Iterable<React__default.ReactNode> | Promise<string | number | bigint | boolean | React__default.ReactPortal | React__default.ReactElement<unknown, string | React__default.JSXElementConstructor<any>> | Iterable<React__default.ReactNode> | null | undefined> | null | undefined;
7938
7946
  declare namespace DataGrid {
7939
7947
  var displayName: string;
7940
7948
  }
@@ -7985,6 +7993,9 @@ interface DataListSwipeAction {
7985
7993
  /** Button variant */
7986
7994
  variant?: 'primary' | 'secondary' | 'danger' | 'ghost';
7987
7995
  }
7996
+ /**
7997
+ * @fieldsContract display
7998
+ */
7988
7999
  interface DataListProps extends DataDndProps {
7989
8000
  /**
7990
8001
  * Schema entity data — the collection of rows to render.
@@ -8087,8 +8098,13 @@ interface DataListProps extends DataDndProps {
8087
8098
  * data-grid / data-list / entity-table share one knob name from authors.
8088
8099
  */
8089
8100
  look?: "dense" | "spacious" | "striped" | "borderless" | "card-rows";
8101
+ /** Relation display data: { fieldName: [{value, label}] } — injected
8102
+ * server-side by the runtime (relation-option injection) or bound by
8103
+ * compiled codegen; resolves stored foreign ids to display names for a
8104
+ * field whose type is relation. Same contract DetailPanel takes. */
8105
+ relationsData?: Record<string, readonly RelationOption[]>;
8090
8106
  }
8091
- declare function DataList({ entity, fields, columns, itemActions, maxInlineActions, itemClickEvent, gap, variant, groupBy, senderField, senderLabelField, currentUser, emptyMessage, className, isLoading, error, reorderable: _reorderable, reorderEvent: _reorderEvent, swipeLeftEvent: _swipeLeftEvent, swipeLeftActions: _swipeLeftActions, swipeRightEvent: _swipeRightEvent, swipeRightActions: _swipeRightActions, longPressEvent: _longPressEvent, infiniteScroll, loadMoreEvent, hasMore, children, pageSize, sortBy, sortDirection, renderItem: schemaRenderItem, dragGroup, accepts, sortable: sortableProp, dropEvent, reorderEvent: dndReorderEvent, positionEvent, dndItemIdField, dndRoot, look, }: DataListProps): string | number | bigint | boolean | React__default.JSX.Element | Iterable<React__default.ReactNode> | Promise<string | number | bigint | boolean | React__default.ReactPortal | React__default.ReactElement<unknown, string | React__default.JSXElementConstructor<any>> | Iterable<React__default.ReactNode> | null | undefined> | null | undefined;
8107
+ declare function DataList({ entity, fields, columns, itemActions, maxInlineActions, itemClickEvent, gap, variant, groupBy, senderField, senderLabelField, currentUser, emptyMessage, className, isLoading, error, reorderable: _reorderable, reorderEvent: _reorderEvent, swipeLeftEvent: _swipeLeftEvent, swipeLeftActions: _swipeLeftActions, swipeRightEvent: _swipeRightEvent, swipeRightActions: _swipeRightActions, longPressEvent: _longPressEvent, infiniteScroll, loadMoreEvent, hasMore, children, pageSize, sortBy, sortDirection, renderItem: schemaRenderItem, dragGroup, accepts, sortable: sortableProp, dropEvent, reorderEvent: dndReorderEvent, positionEvent, dndItemIdField, dndRoot, look, relationsData, }: DataListProps): string | number | bigint | boolean | React__default.JSX.Element | Iterable<React__default.ReactNode> | Promise<string | number | bigint | boolean | React__default.ReactPortal | React__default.ReactElement<unknown, string | React__default.JSXElementConstructor<any>> | Iterable<React__default.ReactNode> | null | undefined> | null | undefined;
8092
8108
  declare namespace DataList {
8093
8109
  var displayName: string;
8094
8110
  }
@@ -8148,6 +8164,7 @@ interface TableViewItemAction {
8148
8164
  * columns, with inline row actions and grouping.
8149
8165
  *
8150
8166
  * @capabilities admin console table, records list, CRUD data table, user list, manage-users grid, sortable columns, row selection with bulk actions, grouped list view
8167
+ * @fieldsContract display
8151
8168
  */
8152
8169
  interface TableViewProps extends DataDndProps {
8153
8170
  /** Schema entity data — the collection of rows to render. */
@@ -8212,8 +8229,13 @@ interface TableViewProps extends DataDndProps {
8212
8229
  * so authors share one knob name across row renderers.
8213
8230
  */
8214
8231
  look?: 'dense' | 'spacious' | 'striped' | 'borderless' | 'bordered';
8232
+ /** Relation display data: { fieldName: [{value, label}] } — injected
8233
+ * server-side by the runtime (relation-option injection) or bound by
8234
+ * compiled codegen; resolves stored foreign ids to display names for a
8235
+ * column whose field is relation-typed. Same contract DetailPanel takes. */
8236
+ relationsData?: Record<string, readonly RelationOption[]>;
8215
8237
  }
8216
- declare function TableView({ entity, columns, fields, itemActions, maxInlineActions, itemClickEvent, selectable, selectEvent, selectedIds, sortEvent, sortColumn, sortDirection, className, emptyMessage, isLoading, error, groupBy, pageSize, children, renderItem: _schemaRenderItem, look, dragGroup, accepts, sortable, dropEvent, reorderEvent, positionEvent, dndItemIdField, dndRoot, }: TableViewProps): React__default.JSX.Element;
8238
+ declare function TableView({ entity, columns, fields, itemActions, maxInlineActions, itemClickEvent, selectable, selectEvent, selectedIds, sortEvent, sortColumn, sortDirection, className, emptyMessage, isLoading, error, groupBy, pageSize, children, renderItem: _schemaRenderItem, look, dragGroup, accepts, sortable, dropEvent, reorderEvent, positionEvent, dndItemIdField, dndRoot, relationsData, }: TableViewProps): React__default.JSX.Element;
8217
8239
  declare namespace TableView {
8218
8240
  var displayName: string;
8219
8241
  }
@@ -11228,6 +11250,9 @@ interface DataTableEmptyAction {
11228
11250
  label: string;
11229
11251
  event?: EventKey;
11230
11252
  }
11253
+ /**
11254
+ * @fieldsContract display
11255
+ */
11231
11256
  interface DataTableProps<T extends EntityRow & {
11232
11257
  id: string | number;
11233
11258
  }> extends DisplayStateProps {
@@ -11268,10 +11293,15 @@ interface DataTableProps<T extends EntityRow & {
11268
11293
  showTotal?: boolean;
11269
11294
  /** Layer 2 visual treatment — orthogonal to the semantic variant. */
11270
11295
  look?: EntityTableLook;
11296
+ /** Relation display data: { fieldName: [{value, label}] } — injected
11297
+ * server-side by the runtime (relation-option injection) or bound by
11298
+ * compiled codegen; resolves stored foreign ids to display names for a
11299
+ * column whose field is relation-typed. Same contract DetailPanel takes. */
11300
+ relationsData?: Record<string, readonly RelationOption[]>;
11271
11301
  }
11272
11302
  declare function DataTable<T extends EntityRow & {
11273
11303
  id: string | number;
11274
- }>({ fields, columns, entity, itemActions, isLoading, error, emptyIcon, emptyTitle, emptyDescription, emptyAction, selectable, selectedIds, sortBy, sortDirection, searchable, searchValue, searchPlaceholder, page, pageSize, totalCount, rowActions: externalRowActions, bulkActions, headerActions, showTotal, className, look, }: DataTableProps<T>): React__default.JSX.Element;
11304
+ }>({ fields, columns, entity, itemActions, isLoading, error, emptyIcon, emptyTitle, emptyDescription, emptyAction, selectable, selectedIds, sortBy, sortDirection, searchable, searchValue, searchPlaceholder, page, pageSize, totalCount, rowActions: externalRowActions, bulkActions, headerActions, showTotal, className, look, relationsData, }: DataTableProps<T>): React__default.JSX.Element;
11275
11305
  declare namespace DataTable {
11276
11306
  var displayName: string;
11277
11307
  }