@almadar/ui 5.124.0 → 5.125.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
- import { M as MappedPattern, C as ClientEffect, q as ClientEffectExecutorConfig, r as NotifyOptions, D as DataContext, s as DataResolution, S as SlotDefinition } from '../offline-executor-aRuNznZx.cjs';
2
- export { E as EventResponse, N as NavigationContextValue, b as NavigationProvider, c as NavigationProviderProps, d as NavigationState, O as OfflineExecutor, t as OfflineExecutorConfig, v as OfflineExecutorState, P as PendingSyncEffect, w as SlotType, a as UseOfflineExecutorOptions, U as UseOfflineExecutorResult, x as createOfflineExecutor, e as extractRouteParams, f as findPageByName, g as findPageByPath, h as getAllPages, i as getDefaultPage, m as matchPath, p as pathMatches, u as useActivePage, j as useInitPayload, k as useNavigateTo, l as useNavigation, n as useNavigationId, o as useNavigationState, y as useOfflineExecutor } from '../offline-executor-aRuNznZx.cjs';
1
+ import { M as MappedPattern, C as ClientEffect, s as ClientEffectExecutorConfig, t as NotifyOptions, D as DataContext, v as DataResolution, S as SlotDefinition } from '../offline-executor-Qz4b6GpF.cjs';
2
+ export { E as EventResponse, N as NavigationContextValue, b as NavigationProvider, c as NavigationProviderProps, d as NavigationState, O as OfflineExecutor, w as OfflineExecutorConfig, x as OfflineExecutorState, P as PendingSyncEffect, y as SlotType, a as UseOfflineExecutorOptions, U as UseOfflineExecutorResult, e as comparePathSpecificity, z as createOfflineExecutor, f as extractRouteParams, g as findPageByName, h as findPageByPath, i as getAllPages, j as getDefaultPage, m as matchPath, k as matchPathAmong, p as pathMatches, u as useActivePage, l as useInitPayload, n as useNavigateTo, o as useNavigation, q as useNavigationId, r as useNavigationState, A as useOfflineExecutor } from '../offline-executor-Qz4b6GpF.cjs';
3
3
  import { PatternPropDef } from '@almadar/core/patterns';
4
4
  import { PatternConfig, FieldValue, EntityRow, UISlot } from '@almadar/core';
5
5
  export { PatternConfig, UISlot } from '@almadar/core';
@@ -1,5 +1,5 @@
1
- import { M as MappedPattern, C as ClientEffect, q as ClientEffectExecutorConfig, r as NotifyOptions, D as DataContext, s as DataResolution, S as SlotDefinition } from '../offline-executor-aRuNznZx.js';
2
- export { E as EventResponse, N as NavigationContextValue, b as NavigationProvider, c as NavigationProviderProps, d as NavigationState, O as OfflineExecutor, t as OfflineExecutorConfig, v as OfflineExecutorState, P as PendingSyncEffect, w as SlotType, a as UseOfflineExecutorOptions, U as UseOfflineExecutorResult, x as createOfflineExecutor, e as extractRouteParams, f as findPageByName, g as findPageByPath, h as getAllPages, i as getDefaultPage, m as matchPath, p as pathMatches, u as useActivePage, j as useInitPayload, k as useNavigateTo, l as useNavigation, n as useNavigationId, o as useNavigationState, y as useOfflineExecutor } from '../offline-executor-aRuNznZx.js';
1
+ import { M as MappedPattern, C as ClientEffect, s as ClientEffectExecutorConfig, t as NotifyOptions, D as DataContext, v as DataResolution, S as SlotDefinition } from '../offline-executor-Qz4b6GpF.js';
2
+ export { E as EventResponse, N as NavigationContextValue, b as NavigationProvider, c as NavigationProviderProps, d as NavigationState, O as OfflineExecutor, w as OfflineExecutorConfig, x as OfflineExecutorState, P as PendingSyncEffect, y as SlotType, a as UseOfflineExecutorOptions, U as UseOfflineExecutorResult, e as comparePathSpecificity, z as createOfflineExecutor, f as extractRouteParams, g as findPageByName, h as findPageByPath, i as getAllPages, j as getDefaultPage, m as matchPath, k as matchPathAmong, p as pathMatches, u as useActivePage, l as useInitPayload, n as useNavigateTo, o as useNavigation, q as useNavigationId, r as useNavigationState, A as useOfflineExecutor } from '../offline-executor-Qz4b6GpF.js';
3
3
  import { PatternPropDef } from '@almadar/core/patterns';
4
4
  import { PatternConfig, FieldValue, EntityRow, UISlot } from '@almadar/core';
5
5
  export { PatternConfig, UISlot } from '@almadar/core';
@@ -1,6 +1,6 @@
1
1
  import { createLogger } from '@almadar/logger';
2
2
  import { createContext, useRef, useCallback, useEffect, useContext, useState } from 'react';
3
- export { NavigationProvider, extractRouteParams, findPageByName, findPageByPath, getAllPages, getDefaultPage, matchPath, pathMatches, useActivePage, useInitPayload, useNavigateTo, useNavigation, useNavigationId, useNavigationState } from '@almadar/ui/providers';
3
+ export { NavigationProvider, comparePathSpecificity, extractRouteParams, findPageByName, findPageByPath, getAllPages, getDefaultPage, matchPath, matchPathAmong, pathMatches, useActivePage, useInitPayload, useNavigateTo, useNavigation, useNavigationId, useNavigationState } from '@almadar/ui/providers';
4
4
  import { componentMapping as componentMapping$1, patternsRegistry } from '@almadar/core/patterns';
5
5
 
6
6
  var __defProp = Object.defineProperty;
@@ -22869,13 +22869,13 @@ function DataGrid({
22869
22869
  }
22870
22870
  ),
22871
22871
  /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", className: "flex-1 min-w-0", children: [
22872
- titleValue !== void 0 && titleValue !== null && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", className: "items-center", children: [
22872
+ titleValue !== void 0 && titleValue !== null && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", className: "items-center min-w-0", children: [
22873
22873
  titleField?.icon && renderIconInput(titleField.icon, { size: "sm", className: "text-primary flex-shrink-0" }),
22874
22874
  /* @__PURE__ */ jsxRuntime.jsx(
22875
22875
  Typography,
22876
22876
  {
22877
22877
  variant: titleField?.variant === "h3" ? "h3" : "h4",
22878
- className: "font-semibold truncate",
22878
+ className: "font-semibold truncate min-w-0",
22879
22879
  children: String(titleValue)
22880
22880
  }
22881
22881
  )
@@ -22933,7 +22933,7 @@ function DataGrid({
22933
22933
  )
22934
22934
  ] }, field.name);
22935
22935
  }) }) }),
22936
- primaryActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "px-4 py-3 mt-auto border-t border-border", children: /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", className: "justify-end", children: [
22936
+ primaryActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "px-4 py-3 mt-auto border-t border-border", children: /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", className: "justify-end flex-wrap", children: [
22937
22937
  (maxInlineActions != null ? primaryActions.slice(0, maxInlineActions) : primaryActions).map((action, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
22938
22938
  Button,
22939
22939
  {
@@ -27882,7 +27882,7 @@ function TableView({
27882
27882
  col.key
27883
27883
  );
27884
27884
  }),
27885
- hasActions && /* @__PURE__ */ jsxRuntime.jsx(Box, { "aria-hidden": true })
27885
+ hasActions && /* @__PURE__ */ jsxRuntime.jsx(Box, { "aria-hidden": true, className: "sticky right-0 bg-[var(--color-surface-subtle)]" })
27886
27886
  ]
27887
27887
  }
27888
27888
  );
@@ -27926,41 +27926,54 @@ function TableView({
27926
27926
  }
27927
27927
  return /* @__PURE__ */ jsxRuntime.jsx(Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: formatCell(raw, col.format) }) }, col.key);
27928
27928
  }),
27929
- itemActions && itemActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", className: "justify-end flex-shrink-0 opacity-60 group-hover:opacity-100 transition-opacity", children: [
27930
- (maxInlineActions != null ? itemActions.slice(0, maxInlineActions) : itemActions).map((action, i) => /* @__PURE__ */ jsxRuntime.jsxs(
27931
- Button,
27932
- {
27933
- variant: action.variant ?? "ghost",
27934
- size: "sm",
27935
- onClick: handleActionClick(action, row),
27936
- "data-testid": `action-${action.event}`,
27937
- "data-row-id": String(row.id),
27938
- className: cn(action.variant === "danger" && "text-error hover:bg-error/10"),
27939
- children: [
27940
- action.icon && renderIconInput3(action.icon, { size: "xs", className: "mr-1" }),
27941
- action.label
27942
- ]
27943
- },
27944
- i
27945
- )),
27946
- maxInlineActions != null && itemActions.length > maxInlineActions && /* @__PURE__ */ jsxRuntime.jsx(
27947
- Menu,
27948
- {
27949
- position: "bottom-end",
27950
- trigger: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
27951
- items: itemActions.slice(maxInlineActions).map((action) => ({
27952
- label: action.label,
27953
- icon: action.icon,
27954
- event: action.event,
27955
- variant: action.variant === "danger" ? "danger" : "default",
27956
- onClick: () => eventBus.emit(`UI:${action.event}`, {
27957
- id: row.id,
27958
- row
27959
- })
27960
- }))
27961
- }
27962
- )
27963
- ] })
27929
+ itemActions && itemActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
27930
+ HStack,
27931
+ {
27932
+ gap: "xs",
27933
+ className: cn(
27934
+ // Pinned: the fixed column tracks routinely overflow the caller's
27935
+ // scroll container, which used to leave the actions off-screen.
27936
+ // Opaque so scrolled cells pass underneath it.
27937
+ "justify-end flex-shrink-0 sticky right-0 z-[1] transition-colors",
27938
+ lk.striped && index % 2 === 1 ? "bg-[var(--color-surface-subtle)]" : "bg-[var(--color-card)] group-hover:bg-[var(--color-surface-subtle)]"
27939
+ ),
27940
+ children: [
27941
+ (maxInlineActions != null ? itemActions.slice(0, maxInlineActions) : itemActions).map((action, i) => /* @__PURE__ */ jsxRuntime.jsxs(
27942
+ Button,
27943
+ {
27944
+ variant: action.variant ?? "ghost",
27945
+ size: "sm",
27946
+ onClick: handleActionClick(action, row),
27947
+ "data-testid": `action-${action.event}`,
27948
+ "data-row-id": String(row.id),
27949
+ className: cn(action.variant === "danger" && "text-error hover:bg-error/10"),
27950
+ children: [
27951
+ action.icon && renderIconInput3(action.icon, { size: "xs", className: "mr-1" }),
27952
+ action.label
27953
+ ]
27954
+ },
27955
+ i
27956
+ )),
27957
+ maxInlineActions != null && itemActions.length > maxInlineActions && /* @__PURE__ */ jsxRuntime.jsx(
27958
+ Menu,
27959
+ {
27960
+ position: "bottom-end",
27961
+ trigger: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
27962
+ items: itemActions.slice(maxInlineActions).map((action) => ({
27963
+ label: action.label,
27964
+ icon: action.icon,
27965
+ event: action.event,
27966
+ variant: action.variant === "danger" ? "danger" : "default",
27967
+ onClick: () => eventBus.emit(`UI:${action.event}`, {
27968
+ id: row.id,
27969
+ row
27970
+ })
27971
+ }))
27972
+ }
27973
+ )
27974
+ ]
27975
+ }
27976
+ )
27964
27977
  ]
27965
27978
  }
27966
27979
  );
@@ -36557,6 +36570,16 @@ function normalizeFieldDefs(fields) {
36557
36570
  return String(f3);
36558
36571
  });
36559
36572
  }
36573
+ function buildFieldLabelMap(fields) {
36574
+ const map = {};
36575
+ if (!fields) return map;
36576
+ for (const f3 of fields) {
36577
+ if (typeof f3 === "object" && "key" in f3 && f3.header !== void 0 && f3.header !== "") {
36578
+ map[f3.key] = f3.header;
36579
+ }
36580
+ }
36581
+ return map;
36582
+ }
36560
36583
  function buildFieldTypeMap(fields) {
36561
36584
  const map = {};
36562
36585
  if (!fields) return map;
@@ -36608,6 +36631,8 @@ var init_DetailPanel = __esm({
36608
36631
  };
36609
36632
  const effectiveFieldNames = isFieldDefArray(propFields) ? normalizeFieldDefs(propFields) : fieldNames;
36610
36633
  const fieldTypeMap = isFieldDefArray(propFields) ? buildFieldTypeMap(propFields) : {};
36634
+ const fieldLabelMap = isFieldDefArray(propFields) ? buildFieldLabelMap(propFields) : {};
36635
+ const labelFor = (field) => fieldLabelMap[field] ?? formatFieldLabel(field);
36611
36636
  const handleActionClick = React82.useCallback(
36612
36637
  (action, data2) => {
36613
36638
  if (action.navigatesTo) {
@@ -36643,7 +36668,7 @@ var init_DetailPanel = __esm({
36643
36668
  if (typeof field === "string") {
36644
36669
  const value = getNestedValue(normalizedData, field);
36645
36670
  return {
36646
- label: formatFieldLabel(field),
36671
+ label: labelFor(field),
36647
36672
  value: formatFieldValue(value, field),
36648
36673
  icon: getFieldIcon(field)
36649
36674
  };
@@ -36654,7 +36679,10 @@ var init_DetailPanel = __esm({
36654
36679
  }
36655
36680
  if (normalizedData && effectiveFieldNames) {
36656
36681
  const primaryField = effectiveFieldNames[0];
36657
- if (!title && primaryField && normalizedData[primaryField]) {
36682
+ const titleDerivedFromPrimary = Boolean(
36683
+ !title && primaryField && normalizedData[primaryField]
36684
+ );
36685
+ if (titleDerivedFromPrimary) {
36658
36686
  title = String(normalizedData[primaryField]);
36659
36687
  }
36660
36688
  const statusFields = effectiveFieldNames.filter(
@@ -36673,16 +36701,16 @@ var init_DetailPanel = __esm({
36673
36701
  (f3) => f3.toLowerCase().includes("description") || f3.toLowerCase().includes("note")
36674
36702
  );
36675
36703
  const otherFields = effectiveFieldNames.filter(
36676
- (f3) => f3 !== primaryField && !statusFields.includes(f3) && !progressFields.includes(f3) && !metricFields.includes(f3) && !dateFields.includes(f3) && !descriptionFields.includes(f3)
36704
+ (f3) => (!titleDerivedFromPrimary || f3 !== primaryField) && !statusFields.includes(f3) && !progressFields.includes(f3) && !metricFields.includes(f3) && !dateFields.includes(f3) && !descriptionFields.includes(f3)
36677
36705
  );
36678
36706
  sections = [];
36679
36707
  if (statusFields.length > 0 || otherFields.length > 0) {
36680
36708
  const overviewFields = [];
36681
- [...statusFields, ...otherFields.slice(0, 3)].forEach((field) => {
36709
+ [...statusFields, ...otherFields].forEach((field) => {
36682
36710
  const value = getNestedValue(normalizedData, field);
36683
36711
  if (value !== void 0 && value !== null) {
36684
36712
  overviewFields.push({
36685
- label: formatFieldLabel(field),
36713
+ label: labelFor(field),
36686
36714
  value: renderRichFieldValue(value, field, fieldTypeMap[field]),
36687
36715
  icon: getFieldIcon(field)
36688
36716
  });
@@ -36698,7 +36726,7 @@ var init_DetailPanel = __esm({
36698
36726
  const value = getNestedValue(normalizedData, field);
36699
36727
  if (value !== void 0 && value !== null) {
36700
36728
  metricsFields.push({
36701
- label: formatFieldLabel(field),
36729
+ label: labelFor(field),
36702
36730
  value: renderRichFieldValue(value, field, fieldTypeMap[field]),
36703
36731
  icon: getFieldIcon(field)
36704
36732
  });
@@ -36714,7 +36742,7 @@ var init_DetailPanel = __esm({
36714
36742
  const value = getNestedValue(normalizedData, field);
36715
36743
  if (value !== void 0 && value !== null) {
36716
36744
  timelineFields.push({
36717
- label: formatFieldLabel(field),
36745
+ label: labelFor(field),
36718
36746
  value: renderRichFieldValue(value, field, fieldTypeMap[field]),
36719
36747
  icon: getFieldIcon(field)
36720
36748
  });
@@ -36730,7 +36758,7 @@ var init_DetailPanel = __esm({
36730
36758
  const value = getNestedValue(normalizedData, field);
36731
36759
  if (value !== void 0 && value !== null) {
36732
36760
  descFields.push({
36733
- label: formatFieldLabel(field),
36761
+ label: labelFor(field),
36734
36762
  value: renderRichFieldValue(value, field, fieldTypeMap[field]),
36735
36763
  icon: getFieldIcon(field)
36736
36764
  });
@@ -36778,7 +36806,7 @@ var init_DetailPanel = __esm({
36778
36806
  if (typeof field === "string") {
36779
36807
  const value = normalizedData ? getNestedValue(normalizedData, field) : void 0;
36780
36808
  allFields.push({
36781
- label: formatFieldLabel(field),
36809
+ label: labelFor(field),
36782
36810
  value: renderRichFieldValue(value, field, fieldTypeMap[field]),
36783
36811
  icon: getFieldIcon(field)
36784
36812
  });
@@ -45908,11 +45936,9 @@ function OrbPreview({
45908
45936
  }, [parsedSchema]);
45909
45937
  const initialPageMatch = React82.useMemo(() => {
45910
45938
  if (!initialPagePath) return void 0;
45911
- for (const { page } of pages) {
45912
- const params = page.path ? providers.matchPath(page.path, initialPagePath) : null;
45913
- if (params !== null) return { name: page.name, params };
45914
- }
45915
- return void 0;
45939
+ const hit = providers.matchPathAmong(pages, initialPagePath, (entry) => entry.page.path);
45940
+ if (!hit) return void 0;
45941
+ return { name: hit.candidate.page.name, params: hit.params };
45916
45942
  }, [pages, initialPagePath]);
45917
45943
  const initialPageName = initialPageMatch?.name;
45918
45944
  const [currentPage, setCurrentPage] = React82.useState(initialPageName);
@@ -45926,16 +45952,9 @@ function OrbPreview({
45926
45952
  }
45927
45953
  }, [initialPagePath, initialPageName, initialPageMatch]);
45928
45954
  const handleNavigate = React82.useCallback((path) => {
45929
- let match;
45930
- let params = {};
45931
- for (const entry of pages) {
45932
- const m = entry.page.path ? providers.matchPath(entry.page.path, path) : null;
45933
- if (m !== null) {
45934
- match = entry;
45935
- params = m;
45936
- break;
45937
- }
45938
- }
45955
+ const hit = providers.matchPathAmong(pages, path, (entry) => entry.page.path);
45956
+ const match = hit?.candidate;
45957
+ const params = hit?.params ?? {};
45939
45958
  navLog.debug("handleNavigate", () => ({
45940
45959
  path,
45941
45960
  matched: match?.page.name ?? null,
@@ -1,6 +1,6 @@
1
1
  import * as React82 from 'react';
2
2
  import React82__default, { createContext, useContext, useMemo, useRef, useEffect, useCallback, Suspense, useState, useSyncExternalStore, useLayoutEffect, lazy, useId } from 'react';
3
- import { EventBusContext, useTraitScopeChain, useEntitySchemaOptional, useEntitySchema, getAllPages, matchPath, OrbitalProvider, TraitScopeProvider, ServerBridgeProvider, useCurrentPagePath, useGameAudioContextOptional, VerificationProvider, EntitySchemaProvider, OrbitalThemeProvider, useServerBridge } from '@almadar/ui/providers';
3
+ import { EventBusContext, useTraitScopeChain, useEntitySchemaOptional, useEntitySchema, getAllPages, matchPathAmong, OrbitalProvider, TraitScopeProvider, ServerBridgeProvider, useCurrentPagePath, useGameAudioContextOptional, VerificationProvider, EntitySchemaProvider, OrbitalThemeProvider, useServerBridge } from '@almadar/ui/providers';
4
4
  export { EntitySchemaProvider, ServerBridgeProvider, TraitContext, TraitProvider, useEntitySchema, useEntitySchemaOptional, useServerBridge, useTrait, useTraitContext } from '@almadar/ui/providers';
5
5
  import { createLogger, setNamespaceLevel, isLogLevelEnabled } from '@almadar/logger';
6
6
  import { clsx } from 'clsx';
@@ -22795,13 +22795,13 @@ function DataGrid({
22795
22795
  }
22796
22796
  ),
22797
22797
  /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: "flex-1 min-w-0", children: [
22798
- titleValue !== void 0 && titleValue !== null && /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center", children: [
22798
+ titleValue !== void 0 && titleValue !== null && /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center min-w-0", children: [
22799
22799
  titleField?.icon && renderIconInput(titleField.icon, { size: "sm", className: "text-primary flex-shrink-0" }),
22800
22800
  /* @__PURE__ */ jsx(
22801
22801
  Typography,
22802
22802
  {
22803
22803
  variant: titleField?.variant === "h3" ? "h3" : "h4",
22804
- className: "font-semibold truncate",
22804
+ className: "font-semibold truncate min-w-0",
22805
22805
  children: String(titleValue)
22806
22806
  }
22807
22807
  )
@@ -22859,7 +22859,7 @@ function DataGrid({
22859
22859
  )
22860
22860
  ] }, field.name);
22861
22861
  }) }) }),
22862
- primaryActions.length > 0 && /* @__PURE__ */ jsx(Box, { className: "px-4 py-3 mt-auto border-t border-border", children: /* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "justify-end", children: [
22862
+ primaryActions.length > 0 && /* @__PURE__ */ jsx(Box, { className: "px-4 py-3 mt-auto border-t border-border", children: /* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "justify-end flex-wrap", children: [
22863
22863
  (maxInlineActions != null ? primaryActions.slice(0, maxInlineActions) : primaryActions).map((action, idx) => /* @__PURE__ */ jsxs(
22864
22864
  Button,
22865
22865
  {
@@ -27808,7 +27808,7 @@ function TableView({
27808
27808
  col.key
27809
27809
  );
27810
27810
  }),
27811
- hasActions && /* @__PURE__ */ jsx(Box, { "aria-hidden": true })
27811
+ hasActions && /* @__PURE__ */ jsx(Box, { "aria-hidden": true, className: "sticky right-0 bg-[var(--color-surface-subtle)]" })
27812
27812
  ]
27813
27813
  }
27814
27814
  );
@@ -27852,41 +27852,54 @@ function TableView({
27852
27852
  }
27853
27853
  return /* @__PURE__ */ jsx(Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsx("span", { className: "truncate", children: formatCell(raw, col.format) }) }, col.key);
27854
27854
  }),
27855
- itemActions && itemActions.length > 0 && /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "justify-end flex-shrink-0 opacity-60 group-hover:opacity-100 transition-opacity", children: [
27856
- (maxInlineActions != null ? itemActions.slice(0, maxInlineActions) : itemActions).map((action, i) => /* @__PURE__ */ jsxs(
27857
- Button,
27858
- {
27859
- variant: action.variant ?? "ghost",
27860
- size: "sm",
27861
- onClick: handleActionClick(action, row),
27862
- "data-testid": `action-${action.event}`,
27863
- "data-row-id": String(row.id),
27864
- className: cn(action.variant === "danger" && "text-error hover:bg-error/10"),
27865
- children: [
27866
- action.icon && renderIconInput3(action.icon, { size: "xs", className: "mr-1" }),
27867
- action.label
27868
- ]
27869
- },
27870
- i
27871
- )),
27872
- maxInlineActions != null && itemActions.length > maxInlineActions && /* @__PURE__ */ jsx(
27873
- Menu,
27874
- {
27875
- position: "bottom-end",
27876
- trigger: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
27877
- items: itemActions.slice(maxInlineActions).map((action) => ({
27878
- label: action.label,
27879
- icon: action.icon,
27880
- event: action.event,
27881
- variant: action.variant === "danger" ? "danger" : "default",
27882
- onClick: () => eventBus.emit(`UI:${action.event}`, {
27883
- id: row.id,
27884
- row
27885
- })
27886
- }))
27887
- }
27888
- )
27889
- ] })
27855
+ itemActions && itemActions.length > 0 && /* @__PURE__ */ jsxs(
27856
+ HStack,
27857
+ {
27858
+ gap: "xs",
27859
+ className: cn(
27860
+ // Pinned: the fixed column tracks routinely overflow the caller's
27861
+ // scroll container, which used to leave the actions off-screen.
27862
+ // Opaque so scrolled cells pass underneath it.
27863
+ "justify-end flex-shrink-0 sticky right-0 z-[1] transition-colors",
27864
+ lk.striped && index % 2 === 1 ? "bg-[var(--color-surface-subtle)]" : "bg-[var(--color-card)] group-hover:bg-[var(--color-surface-subtle)]"
27865
+ ),
27866
+ children: [
27867
+ (maxInlineActions != null ? itemActions.slice(0, maxInlineActions) : itemActions).map((action, i) => /* @__PURE__ */ jsxs(
27868
+ Button,
27869
+ {
27870
+ variant: action.variant ?? "ghost",
27871
+ size: "sm",
27872
+ onClick: handleActionClick(action, row),
27873
+ "data-testid": `action-${action.event}`,
27874
+ "data-row-id": String(row.id),
27875
+ className: cn(action.variant === "danger" && "text-error hover:bg-error/10"),
27876
+ children: [
27877
+ action.icon && renderIconInput3(action.icon, { size: "xs", className: "mr-1" }),
27878
+ action.label
27879
+ ]
27880
+ },
27881
+ i
27882
+ )),
27883
+ maxInlineActions != null && itemActions.length > maxInlineActions && /* @__PURE__ */ jsx(
27884
+ Menu,
27885
+ {
27886
+ position: "bottom-end",
27887
+ trigger: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
27888
+ items: itemActions.slice(maxInlineActions).map((action) => ({
27889
+ label: action.label,
27890
+ icon: action.icon,
27891
+ event: action.event,
27892
+ variant: action.variant === "danger" ? "danger" : "default",
27893
+ onClick: () => eventBus.emit(`UI:${action.event}`, {
27894
+ id: row.id,
27895
+ row
27896
+ })
27897
+ }))
27898
+ }
27899
+ )
27900
+ ]
27901
+ }
27902
+ )
27890
27903
  ]
27891
27904
  }
27892
27905
  );
@@ -36483,6 +36496,16 @@ function normalizeFieldDefs(fields) {
36483
36496
  return String(f3);
36484
36497
  });
36485
36498
  }
36499
+ function buildFieldLabelMap(fields) {
36500
+ const map = {};
36501
+ if (!fields) return map;
36502
+ for (const f3 of fields) {
36503
+ if (typeof f3 === "object" && "key" in f3 && f3.header !== void 0 && f3.header !== "") {
36504
+ map[f3.key] = f3.header;
36505
+ }
36506
+ }
36507
+ return map;
36508
+ }
36486
36509
  function buildFieldTypeMap(fields) {
36487
36510
  const map = {};
36488
36511
  if (!fields) return map;
@@ -36534,6 +36557,8 @@ var init_DetailPanel = __esm({
36534
36557
  };
36535
36558
  const effectiveFieldNames = isFieldDefArray(propFields) ? normalizeFieldDefs(propFields) : fieldNames;
36536
36559
  const fieldTypeMap = isFieldDefArray(propFields) ? buildFieldTypeMap(propFields) : {};
36560
+ const fieldLabelMap = isFieldDefArray(propFields) ? buildFieldLabelMap(propFields) : {};
36561
+ const labelFor = (field) => fieldLabelMap[field] ?? formatFieldLabel(field);
36537
36562
  const handleActionClick = useCallback(
36538
36563
  (action, data2) => {
36539
36564
  if (action.navigatesTo) {
@@ -36569,7 +36594,7 @@ var init_DetailPanel = __esm({
36569
36594
  if (typeof field === "string") {
36570
36595
  const value = getNestedValue(normalizedData, field);
36571
36596
  return {
36572
- label: formatFieldLabel(field),
36597
+ label: labelFor(field),
36573
36598
  value: formatFieldValue(value, field),
36574
36599
  icon: getFieldIcon(field)
36575
36600
  };
@@ -36580,7 +36605,10 @@ var init_DetailPanel = __esm({
36580
36605
  }
36581
36606
  if (normalizedData && effectiveFieldNames) {
36582
36607
  const primaryField = effectiveFieldNames[0];
36583
- if (!title && primaryField && normalizedData[primaryField]) {
36608
+ const titleDerivedFromPrimary = Boolean(
36609
+ !title && primaryField && normalizedData[primaryField]
36610
+ );
36611
+ if (titleDerivedFromPrimary) {
36584
36612
  title = String(normalizedData[primaryField]);
36585
36613
  }
36586
36614
  const statusFields = effectiveFieldNames.filter(
@@ -36599,16 +36627,16 @@ var init_DetailPanel = __esm({
36599
36627
  (f3) => f3.toLowerCase().includes("description") || f3.toLowerCase().includes("note")
36600
36628
  );
36601
36629
  const otherFields = effectiveFieldNames.filter(
36602
- (f3) => f3 !== primaryField && !statusFields.includes(f3) && !progressFields.includes(f3) && !metricFields.includes(f3) && !dateFields.includes(f3) && !descriptionFields.includes(f3)
36630
+ (f3) => (!titleDerivedFromPrimary || f3 !== primaryField) && !statusFields.includes(f3) && !progressFields.includes(f3) && !metricFields.includes(f3) && !dateFields.includes(f3) && !descriptionFields.includes(f3)
36603
36631
  );
36604
36632
  sections = [];
36605
36633
  if (statusFields.length > 0 || otherFields.length > 0) {
36606
36634
  const overviewFields = [];
36607
- [...statusFields, ...otherFields.slice(0, 3)].forEach((field) => {
36635
+ [...statusFields, ...otherFields].forEach((field) => {
36608
36636
  const value = getNestedValue(normalizedData, field);
36609
36637
  if (value !== void 0 && value !== null) {
36610
36638
  overviewFields.push({
36611
- label: formatFieldLabel(field),
36639
+ label: labelFor(field),
36612
36640
  value: renderRichFieldValue(value, field, fieldTypeMap[field]),
36613
36641
  icon: getFieldIcon(field)
36614
36642
  });
@@ -36624,7 +36652,7 @@ var init_DetailPanel = __esm({
36624
36652
  const value = getNestedValue(normalizedData, field);
36625
36653
  if (value !== void 0 && value !== null) {
36626
36654
  metricsFields.push({
36627
- label: formatFieldLabel(field),
36655
+ label: labelFor(field),
36628
36656
  value: renderRichFieldValue(value, field, fieldTypeMap[field]),
36629
36657
  icon: getFieldIcon(field)
36630
36658
  });
@@ -36640,7 +36668,7 @@ var init_DetailPanel = __esm({
36640
36668
  const value = getNestedValue(normalizedData, field);
36641
36669
  if (value !== void 0 && value !== null) {
36642
36670
  timelineFields.push({
36643
- label: formatFieldLabel(field),
36671
+ label: labelFor(field),
36644
36672
  value: renderRichFieldValue(value, field, fieldTypeMap[field]),
36645
36673
  icon: getFieldIcon(field)
36646
36674
  });
@@ -36656,7 +36684,7 @@ var init_DetailPanel = __esm({
36656
36684
  const value = getNestedValue(normalizedData, field);
36657
36685
  if (value !== void 0 && value !== null) {
36658
36686
  descFields.push({
36659
- label: formatFieldLabel(field),
36687
+ label: labelFor(field),
36660
36688
  value: renderRichFieldValue(value, field, fieldTypeMap[field]),
36661
36689
  icon: getFieldIcon(field)
36662
36690
  });
@@ -36704,7 +36732,7 @@ var init_DetailPanel = __esm({
36704
36732
  if (typeof field === "string") {
36705
36733
  const value = normalizedData ? getNestedValue(normalizedData, field) : void 0;
36706
36734
  allFields.push({
36707
- label: formatFieldLabel(field),
36735
+ label: labelFor(field),
36708
36736
  value: renderRichFieldValue(value, field, fieldTypeMap[field]),
36709
36737
  icon: getFieldIcon(field)
36710
36738
  });
@@ -45834,11 +45862,9 @@ function OrbPreview({
45834
45862
  }, [parsedSchema]);
45835
45863
  const initialPageMatch = useMemo(() => {
45836
45864
  if (!initialPagePath) return void 0;
45837
- for (const { page } of pages) {
45838
- const params = page.path ? matchPath(page.path, initialPagePath) : null;
45839
- if (params !== null) return { name: page.name, params };
45840
- }
45841
- return void 0;
45865
+ const hit = matchPathAmong(pages, initialPagePath, (entry) => entry.page.path);
45866
+ if (!hit) return void 0;
45867
+ return { name: hit.candidate.page.name, params: hit.params };
45842
45868
  }, [pages, initialPagePath]);
45843
45869
  const initialPageName = initialPageMatch?.name;
45844
45870
  const [currentPage, setCurrentPage] = useState(initialPageName);
@@ -45852,16 +45878,9 @@ function OrbPreview({
45852
45878
  }
45853
45879
  }, [initialPagePath, initialPageName, initialPageMatch]);
45854
45880
  const handleNavigate = useCallback((path) => {
45855
- let match;
45856
- let params = {};
45857
- for (const entry of pages) {
45858
- const m = entry.page.path ? matchPath(entry.page.path, path) : null;
45859
- if (m !== null) {
45860
- match = entry;
45861
- params = m;
45862
- break;
45863
- }
45864
- }
45881
+ const hit = matchPathAmong(pages, path, (entry) => entry.page.path);
45882
+ const match = hit?.candidate;
45883
+ const params = hit?.params ?? {};
45865
45884
  navLog.debug("handleNavigate", () => ({
45866
45885
  path,
45867
45886
  matched: match?.page.name ?? null,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@almadar/ui",
3
- "version": "5.124.0",
3
+ "version": "5.125.0",
4
4
  "description": "React UI components, hooks, and providers for Almadar",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -117,12 +117,12 @@
117
117
  "access": "public"
118
118
  },
119
119
  "dependencies": {
120
- "@almadar/core": "^10.33.0",
121
- "@almadar/evaluator": "^2.34.0",
122
- "@almadar/logger": "^1.9.0",
123
- "@almadar/runtime": "^6.36.0",
124
- "@almadar/std": "^16.143.0",
125
- "@almadar/syntax": "^1.11.0",
120
+ "@almadar/core": "^10.34.0",
121
+ "@almadar/evaluator": "^2.35.0",
122
+ "@almadar/logger": "^1.10.0",
123
+ "@almadar/runtime": "^6.37.0",
124
+ "@almadar/std": "^16.144.0",
125
+ "@almadar/syntax": "^1.12.0",
126
126
  "@dnd-kit/core": "^6.3.1",
127
127
  "@dnd-kit/sortable": "^10.0.0",
128
128
  "@dnd-kit/utilities": "^3.2.2",
@@ -208,9 +208,10 @@
208
208
  "tsup": "^8.0.0",
209
209
  "tsx": "^4.7.0",
210
210
  "turbo": "^2.8.17",
211
- "typescript": "^5.4.0",
212
211
  "vite": "^5.2.0",
213
- "vitest": "^3.2.6"
212
+ "vitest": "^3.2.6",
213
+ "@typescript/native": "npm:typescript@^7.0.2",
214
+ "typescript": "npm:@typescript/typescript6@^6.0.2"
214
215
  },
215
216
  "repository": {
216
217
  "type": "git",