@almadar/ui 5.124.0 → 5.126.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.
@@ -18557,17 +18557,27 @@ function getWeekDays(start) {
18557
18557
  }
18558
18558
  return days;
18559
18559
  }
18560
- function generateDefaultTimeSlots() {
18560
+ function generateDefaultTimeSlots(events2) {
18561
+ let first = DEFAULT_FIRST_HOUR;
18562
+ let last = DEFAULT_LAST_HOUR;
18563
+ for (const ev of events2) {
18564
+ const start = new Date(ev.startTime);
18565
+ if (Number.isNaN(start.getTime())) continue;
18566
+ const hour = start.getHours();
18567
+ if (hour < first) first = hour;
18568
+ if (hour > last) last = hour;
18569
+ }
18561
18570
  const slots = [];
18562
- for (let hour = 9; hour <= 17; hour++) {
18563
- slots.push(`${hour.toString().padStart(2, "0")}:00`);
18571
+ for (let hour = first; hour <= last; hour++) {
18572
+ slots.push(slotLabel(hour));
18564
18573
  }
18565
18574
  return slots;
18566
18575
  }
18567
18576
  function eventInSlot(event, day, slotTime) {
18568
18577
  const eventStart = new Date(event.startTime);
18569
- const [slotHour, slotMinute] = slotTime.split(":").map(Number);
18570
- return eventStart.toDateString() === day.toDateString() && eventStart.getHours() === slotHour && eventStart.getMinutes() === slotMinute;
18578
+ if (Number.isNaN(eventStart.getTime())) return false;
18579
+ const [slotHour] = slotTime.split(":").map(Number);
18580
+ return eventStart.toDateString() === day.toDateString() && eventStart.getHours() === slotHour;
18571
18581
  }
18572
18582
  function CalendarGrid({
18573
18583
  weekStart,
@@ -18596,8 +18606,8 @@ function CalendarGrid({
18596
18606
  [resolvedWeekStart]
18597
18607
  );
18598
18608
  const resolvedTimeSlots = React82.useMemo(
18599
- () => timeSlots ?? generateDefaultTimeSlots(),
18600
- [timeSlots]
18609
+ () => timeSlots ?? generateDefaultTimeSlots(evs),
18610
+ [timeSlots, evs]
18601
18611
  );
18602
18612
  const visibleCount = useDayWindow(dayWindow);
18603
18613
  const [dayOffset, setDayOffset] = React82.useState(0);
@@ -18784,7 +18794,7 @@ function CalendarGrid({
18784
18794
  }
18785
18795
  );
18786
18796
  }
18787
- var SHORT_DATE;
18797
+ var SHORT_DATE, DEFAULT_FIRST_HOUR, DEFAULT_LAST_HOUR, slotLabel;
18788
18798
  var init_CalendarGrid = __esm({
18789
18799
  "components/core/molecules/CalendarGrid.tsx"() {
18790
18800
  "use client";
@@ -18799,6 +18809,9 @@ var init_CalendarGrid = __esm({
18799
18809
  init_useEventBus();
18800
18810
  init_useSwipeGesture();
18801
18811
  SHORT_DATE = { month: "short", day: "numeric" };
18812
+ DEFAULT_FIRST_HOUR = 9;
18813
+ DEFAULT_LAST_HOUR = 17;
18814
+ slotLabel = (hour) => `${hour.toString().padStart(2, "0")}:00`;
18802
18815
  CalendarGrid.displayName = "CalendarGrid";
18803
18816
  }
18804
18817
  });
@@ -22869,13 +22882,13 @@ function DataGrid({
22869
22882
  }
22870
22883
  ),
22871
22884
  /* @__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: [
22885
+ titleValue !== void 0 && titleValue !== null && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", className: "items-center min-w-0", children: [
22873
22886
  titleField?.icon && renderIconInput(titleField.icon, { size: "sm", className: "text-primary flex-shrink-0" }),
22874
22887
  /* @__PURE__ */ jsxRuntime.jsx(
22875
22888
  Typography,
22876
22889
  {
22877
22890
  variant: titleField?.variant === "h3" ? "h3" : "h4",
22878
- className: "font-semibold truncate",
22891
+ className: "font-semibold truncate min-w-0",
22879
22892
  children: String(titleValue)
22880
22893
  }
22881
22894
  )
@@ -22933,7 +22946,7 @@ function DataGrid({
22933
22946
  )
22934
22947
  ] }, field.name);
22935
22948
  }) }) }),
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: [
22949
+ 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
22950
  (maxInlineActions != null ? primaryActions.slice(0, maxInlineActions) : primaryActions).map((action, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
22938
22951
  Button,
22939
22952
  {
@@ -27882,7 +27895,7 @@ function TableView({
27882
27895
  col.key
27883
27896
  );
27884
27897
  }),
27885
- hasActions && /* @__PURE__ */ jsxRuntime.jsx(Box, { "aria-hidden": true })
27898
+ hasActions && /* @__PURE__ */ jsxRuntime.jsx(Box, { "aria-hidden": true, className: "sticky right-0 bg-[var(--color-surface-subtle)]" })
27886
27899
  ]
27887
27900
  }
27888
27901
  );
@@ -27926,41 +27939,54 @@ function TableView({
27926
27939
  }
27927
27940
  return /* @__PURE__ */ jsxRuntime.jsx(Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: formatCell(raw, col.format) }) }, col.key);
27928
27941
  }),
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
- ] })
27942
+ itemActions && itemActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
27943
+ HStack,
27944
+ {
27945
+ gap: "xs",
27946
+ className: cn(
27947
+ // Pinned: the fixed column tracks routinely overflow the caller's
27948
+ // scroll container, which used to leave the actions off-screen.
27949
+ // Opaque so scrolled cells pass underneath it.
27950
+ "justify-end flex-shrink-0 sticky right-0 z-[1] transition-colors",
27951
+ lk.striped && index % 2 === 1 ? "bg-[var(--color-surface-subtle)]" : "bg-[var(--color-card)] group-hover:bg-[var(--color-surface-subtle)]"
27952
+ ),
27953
+ children: [
27954
+ (maxInlineActions != null ? itemActions.slice(0, maxInlineActions) : itemActions).map((action, i) => /* @__PURE__ */ jsxRuntime.jsxs(
27955
+ Button,
27956
+ {
27957
+ variant: action.variant ?? "ghost",
27958
+ size: "sm",
27959
+ onClick: handleActionClick(action, row),
27960
+ "data-testid": `action-${action.event}`,
27961
+ "data-row-id": String(row.id),
27962
+ className: cn(action.variant === "danger" && "text-error hover:bg-error/10"),
27963
+ children: [
27964
+ action.icon && renderIconInput3(action.icon, { size: "xs", className: "mr-1" }),
27965
+ action.label
27966
+ ]
27967
+ },
27968
+ i
27969
+ )),
27970
+ maxInlineActions != null && itemActions.length > maxInlineActions && /* @__PURE__ */ jsxRuntime.jsx(
27971
+ Menu,
27972
+ {
27973
+ position: "bottom-end",
27974
+ 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" }) }),
27975
+ items: itemActions.slice(maxInlineActions).map((action) => ({
27976
+ label: action.label,
27977
+ icon: action.icon,
27978
+ event: action.event,
27979
+ variant: action.variant === "danger" ? "danger" : "default",
27980
+ onClick: () => eventBus.emit(`UI:${action.event}`, {
27981
+ id: row.id,
27982
+ row
27983
+ })
27984
+ }))
27985
+ }
27986
+ )
27987
+ ]
27988
+ }
27989
+ )
27964
27990
  ]
27965
27991
  }
27966
27992
  );
@@ -36557,6 +36583,16 @@ function normalizeFieldDefs(fields) {
36557
36583
  return String(f3);
36558
36584
  });
36559
36585
  }
36586
+ function buildFieldLabelMap(fields) {
36587
+ const map = {};
36588
+ if (!fields) return map;
36589
+ for (const f3 of fields) {
36590
+ if (typeof f3 === "object" && "key" in f3 && f3.header !== void 0 && f3.header !== "") {
36591
+ map[f3.key] = f3.header;
36592
+ }
36593
+ }
36594
+ return map;
36595
+ }
36560
36596
  function buildFieldTypeMap(fields) {
36561
36597
  const map = {};
36562
36598
  if (!fields) return map;
@@ -36608,6 +36644,8 @@ var init_DetailPanel = __esm({
36608
36644
  };
36609
36645
  const effectiveFieldNames = isFieldDefArray(propFields) ? normalizeFieldDefs(propFields) : fieldNames;
36610
36646
  const fieldTypeMap = isFieldDefArray(propFields) ? buildFieldTypeMap(propFields) : {};
36647
+ const fieldLabelMap = isFieldDefArray(propFields) ? buildFieldLabelMap(propFields) : {};
36648
+ const labelFor = (field) => fieldLabelMap[field] ?? formatFieldLabel(field);
36611
36649
  const handleActionClick = React82.useCallback(
36612
36650
  (action, data2) => {
36613
36651
  if (action.navigatesTo) {
@@ -36643,7 +36681,7 @@ var init_DetailPanel = __esm({
36643
36681
  if (typeof field === "string") {
36644
36682
  const value = getNestedValue(normalizedData, field);
36645
36683
  return {
36646
- label: formatFieldLabel(field),
36684
+ label: labelFor(field),
36647
36685
  value: formatFieldValue(value, field),
36648
36686
  icon: getFieldIcon(field)
36649
36687
  };
@@ -36654,7 +36692,10 @@ var init_DetailPanel = __esm({
36654
36692
  }
36655
36693
  if (normalizedData && effectiveFieldNames) {
36656
36694
  const primaryField = effectiveFieldNames[0];
36657
- if (!title && primaryField && normalizedData[primaryField]) {
36695
+ const titleDerivedFromPrimary = Boolean(
36696
+ !title && primaryField && normalizedData[primaryField]
36697
+ );
36698
+ if (titleDerivedFromPrimary) {
36658
36699
  title = String(normalizedData[primaryField]);
36659
36700
  }
36660
36701
  const statusFields = effectiveFieldNames.filter(
@@ -36673,16 +36714,16 @@ var init_DetailPanel = __esm({
36673
36714
  (f3) => f3.toLowerCase().includes("description") || f3.toLowerCase().includes("note")
36674
36715
  );
36675
36716
  const otherFields = effectiveFieldNames.filter(
36676
- (f3) => f3 !== primaryField && !statusFields.includes(f3) && !progressFields.includes(f3) && !metricFields.includes(f3) && !dateFields.includes(f3) && !descriptionFields.includes(f3)
36717
+ (f3) => (!titleDerivedFromPrimary || f3 !== primaryField) && !statusFields.includes(f3) && !progressFields.includes(f3) && !metricFields.includes(f3) && !dateFields.includes(f3) && !descriptionFields.includes(f3)
36677
36718
  );
36678
36719
  sections = [];
36679
36720
  if (statusFields.length > 0 || otherFields.length > 0) {
36680
36721
  const overviewFields = [];
36681
- [...statusFields, ...otherFields.slice(0, 3)].forEach((field) => {
36722
+ [...statusFields, ...otherFields].forEach((field) => {
36682
36723
  const value = getNestedValue(normalizedData, field);
36683
36724
  if (value !== void 0 && value !== null) {
36684
36725
  overviewFields.push({
36685
- label: formatFieldLabel(field),
36726
+ label: labelFor(field),
36686
36727
  value: renderRichFieldValue(value, field, fieldTypeMap[field]),
36687
36728
  icon: getFieldIcon(field)
36688
36729
  });
@@ -36698,7 +36739,7 @@ var init_DetailPanel = __esm({
36698
36739
  const value = getNestedValue(normalizedData, field);
36699
36740
  if (value !== void 0 && value !== null) {
36700
36741
  metricsFields.push({
36701
- label: formatFieldLabel(field),
36742
+ label: labelFor(field),
36702
36743
  value: renderRichFieldValue(value, field, fieldTypeMap[field]),
36703
36744
  icon: getFieldIcon(field)
36704
36745
  });
@@ -36714,7 +36755,7 @@ var init_DetailPanel = __esm({
36714
36755
  const value = getNestedValue(normalizedData, field);
36715
36756
  if (value !== void 0 && value !== null) {
36716
36757
  timelineFields.push({
36717
- label: formatFieldLabel(field),
36758
+ label: labelFor(field),
36718
36759
  value: renderRichFieldValue(value, field, fieldTypeMap[field]),
36719
36760
  icon: getFieldIcon(field)
36720
36761
  });
@@ -36730,7 +36771,7 @@ var init_DetailPanel = __esm({
36730
36771
  const value = getNestedValue(normalizedData, field);
36731
36772
  if (value !== void 0 && value !== null) {
36732
36773
  descFields.push({
36733
- label: formatFieldLabel(field),
36774
+ label: labelFor(field),
36734
36775
  value: renderRichFieldValue(value, field, fieldTypeMap[field]),
36735
36776
  icon: getFieldIcon(field)
36736
36777
  });
@@ -36778,7 +36819,7 @@ var init_DetailPanel = __esm({
36778
36819
  if (typeof field === "string") {
36779
36820
  const value = normalizedData ? getNestedValue(normalizedData, field) : void 0;
36780
36821
  allFields.push({
36781
- label: formatFieldLabel(field),
36822
+ label: labelFor(field),
36782
36823
  value: renderRichFieldValue(value, field, fieldTypeMap[field]),
36783
36824
  icon: getFieldIcon(field)
36784
36825
  });
@@ -43300,6 +43341,13 @@ function MaybeTraitScope({
43300
43341
  if (wrap) {
43301
43342
  return /* @__PURE__ */ jsxRuntime.jsx(providers.TraitScopeProvider, { orbital, trait: sourceTrait, children });
43302
43343
  }
43344
+ if (sourceTrait !== void 0 && schemaCtx !== null) {
43345
+ scopeWrapLog.warn("decline", {
43346
+ sourceTrait,
43347
+ orbitalsByTraitSize: schemaCtx.orbitalsByTrait.size,
43348
+ reason: "sourceTrait not in orbitalsByTrait \u2014 children render unscoped"
43349
+ });
43350
+ }
43303
43351
  return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
43304
43352
  }
43305
43353
  function UISlotComponent({
@@ -43597,7 +43645,7 @@ function renderPatternChildren(children, onDismiss, parentId = "root", parentPat
43597
43645
  const childId = `${parentId}-${index}`;
43598
43646
  const childPath = parentPath === "root" ? `root.children.${index}` : `${parentPath}.children.${index}`;
43599
43647
  const childAsRecord = child;
43600
- const { type: _childType, props: nestedProps, _id: _childNodeId, children: _childChildren, ...flatProps } = childAsRecord;
43648
+ const { type: _childType, props: nestedProps, _id: _childNodeId, _sourceTrait: childSourceTrait, children: _childChildren, ...flatProps } = childAsRecord;
43601
43649
  const resolvedProps = nestedProps !== void 0 ? nestedProps : flatProps;
43602
43650
  if (_childChildren !== void 0 && nestedProps === void 0) {
43603
43651
  resolvedProps.children = _childChildren;
@@ -43612,11 +43660,14 @@ function renderPatternChildren(children, onDismiss, parentId = "root", parentPat
43612
43660
  // (e.g. form-section inside a stack) can resolve entityDef via
43613
43661
  // the trait's linkedEntity for form-field enrichment. The orbCtx
43614
43662
  // (slot/transition/state/entity) propagates the same way so every
43615
- // nested node carries a complete contextual-edit address.
43616
- ...sourceTrait !== void 0 && { sourceTrait },
43663
+ // nested node carries a complete contextual-edit address. A child
43664
+ // carrying its own `_sourceTrait` (multi-source slot stack) owns it
43665
+ // outright — inheriting the synthetic wrapper's sentinel instead
43666
+ // would erase the real owner.
43667
+ ...childSourceTrait !== void 0 ? { sourceTrait: childSourceTrait } : sourceTrait !== void 0 && { sourceTrait },
43617
43668
  ...orbCtx ?? {}
43618
43669
  };
43619
- return /* @__PURE__ */ jsxRuntime.jsx(
43670
+ const renderedChild = /* @__PURE__ */ jsxRuntime.jsx(
43620
43671
  SlotContentRenderer,
43621
43672
  {
43622
43673
  content: childContent,
@@ -43625,6 +43676,7 @@ function renderPatternChildren(children, onDismiss, parentId = "root", parentPat
43625
43676
  },
43626
43677
  childId
43627
43678
  );
43679
+ return childSourceTrait !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(MaybeTraitScope, { sourceTrait: childSourceTrait, children: renderedChild }, childId) : renderedChild;
43628
43680
  });
43629
43681
  }
43630
43682
  function toDrawableNodes(children) {
@@ -45908,11 +45960,9 @@ function OrbPreview({
45908
45960
  }, [parsedSchema]);
45909
45961
  const initialPageMatch = React82.useMemo(() => {
45910
45962
  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;
45963
+ const hit = providers.matchPathAmong(pages, initialPagePath, (entry) => entry.page.path);
45964
+ if (!hit) return void 0;
45965
+ return { name: hit.candidate.page.name, params: hit.params };
45916
45966
  }, [pages, initialPagePath]);
45917
45967
  const initialPageName = initialPageMatch?.name;
45918
45968
  const [currentPage, setCurrentPage] = React82.useState(initialPageName);
@@ -45926,16 +45976,9 @@ function OrbPreview({
45926
45976
  }
45927
45977
  }, [initialPagePath, initialPageName, initialPageMatch]);
45928
45978
  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
- }
45979
+ const hit = providers.matchPathAmong(pages, path, (entry) => entry.page.path);
45980
+ const match = hit?.candidate;
45981
+ const params = hit?.params ?? {};
45939
45982
  navLog.debug("handleNavigate", () => ({
45940
45983
  path,
45941
45984
  matched: match?.page.name ?? null,