@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.
@@ -14601,17 +14601,27 @@ function getWeekDays(start) {
14601
14601
  }
14602
14602
  return days;
14603
14603
  }
14604
- function generateDefaultTimeSlots() {
14604
+ function generateDefaultTimeSlots(events2) {
14605
+ let first = DEFAULT_FIRST_HOUR;
14606
+ let last = DEFAULT_LAST_HOUR;
14607
+ for (const ev of events2) {
14608
+ const start = new Date(ev.startTime);
14609
+ if (Number.isNaN(start.getTime())) continue;
14610
+ const hour = start.getHours();
14611
+ if (hour < first) first = hour;
14612
+ if (hour > last) last = hour;
14613
+ }
14605
14614
  const slots = [];
14606
- for (let hour = 9; hour <= 17; hour++) {
14607
- slots.push(`${hour.toString().padStart(2, "0")}:00`);
14615
+ for (let hour = first; hour <= last; hour++) {
14616
+ slots.push(slotLabel(hour));
14608
14617
  }
14609
14618
  return slots;
14610
14619
  }
14611
14620
  function eventInSlot(event, day, slotTime) {
14612
14621
  const eventStart = new Date(event.startTime);
14613
- const [slotHour, slotMinute] = slotTime.split(":").map(Number);
14614
- return eventStart.toDateString() === day.toDateString() && eventStart.getHours() === slotHour && eventStart.getMinutes() === slotMinute;
14622
+ if (Number.isNaN(eventStart.getTime())) return false;
14623
+ const [slotHour] = slotTime.split(":").map(Number);
14624
+ return eventStart.toDateString() === day.toDateString() && eventStart.getHours() === slotHour;
14615
14625
  }
14616
14626
  function CalendarGrid({
14617
14627
  weekStart,
@@ -14640,8 +14650,8 @@ function CalendarGrid({
14640
14650
  [resolvedWeekStart]
14641
14651
  );
14642
14652
  const resolvedTimeSlots = React74.useMemo(
14643
- () => timeSlots ?? generateDefaultTimeSlots(),
14644
- [timeSlots]
14653
+ () => timeSlots ?? generateDefaultTimeSlots(evs),
14654
+ [timeSlots, evs]
14645
14655
  );
14646
14656
  const visibleCount = useDayWindow(dayWindow);
14647
14657
  const [dayOffset, setDayOffset] = React74.useState(0);
@@ -14828,7 +14838,7 @@ function CalendarGrid({
14828
14838
  }
14829
14839
  );
14830
14840
  }
14831
- var SHORT_DATE;
14841
+ var SHORT_DATE, DEFAULT_FIRST_HOUR, DEFAULT_LAST_HOUR, slotLabel;
14832
14842
  var init_CalendarGrid = __esm({
14833
14843
  "components/core/molecules/CalendarGrid.tsx"() {
14834
14844
  "use client";
@@ -14843,6 +14853,9 @@ var init_CalendarGrid = __esm({
14843
14853
  init_useEventBus();
14844
14854
  init_useSwipeGesture();
14845
14855
  SHORT_DATE = { month: "short", day: "numeric" };
14856
+ DEFAULT_FIRST_HOUR = 9;
14857
+ DEFAULT_LAST_HOUR = 17;
14858
+ slotLabel = (hour) => `${hour.toString().padStart(2, "0")}:00`;
14846
14859
  CalendarGrid.displayName = "CalendarGrid";
14847
14860
  }
14848
14861
  });
@@ -20895,13 +20908,13 @@ function DataGrid({
20895
20908
  }
20896
20909
  ),
20897
20910
  /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "xs", className: "flex-1 min-w-0", children: [
20898
- titleValue !== void 0 && titleValue !== null && /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "xs", className: "items-center", children: [
20911
+ titleValue !== void 0 && titleValue !== null && /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "xs", className: "items-center min-w-0", children: [
20899
20912
  titleField?.icon && renderIconInput(titleField.icon, { size: "sm", className: "text-primary flex-shrink-0" }),
20900
20913
  /* @__PURE__ */ jsxRuntime.jsx(
20901
20914
  exports.Typography,
20902
20915
  {
20903
20916
  variant: titleField?.variant === "h3" ? "h3" : "h4",
20904
- className: "font-semibold truncate",
20917
+ className: "font-semibold truncate min-w-0",
20905
20918
  children: String(titleValue)
20906
20919
  }
20907
20920
  )
@@ -20959,7 +20972,7 @@ function DataGrid({
20959
20972
  )
20960
20973
  ] }, field.name);
20961
20974
  }) }) }),
20962
- primaryActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "px-4 py-3 mt-auto border-t border-border", children: /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "sm", className: "justify-end", children: [
20975
+ primaryActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "px-4 py-3 mt-auto border-t border-border", children: /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "sm", className: "justify-end flex-wrap", children: [
20963
20976
  (maxInlineActions != null ? primaryActions.slice(0, maxInlineActions) : primaryActions).map((action, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
20964
20977
  exports.Button,
20965
20978
  {
@@ -29626,7 +29639,7 @@ function TableView({
29626
29639
  col.key
29627
29640
  );
29628
29641
  }),
29629
- hasActions && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { "aria-hidden": true })
29642
+ hasActions && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { "aria-hidden": true, className: "sticky right-0 bg-[var(--color-surface-subtle)]" })
29630
29643
  ]
29631
29644
  }
29632
29645
  );
@@ -29670,41 +29683,54 @@ function TableView({
29670
29683
  }
29671
29684
  return /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: formatCell(raw, col.format) }) }, col.key);
29672
29685
  }),
29673
- itemActions && itemActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "xs", className: "justify-end flex-shrink-0 opacity-60 group-hover:opacity-100 transition-opacity", children: [
29674
- (maxInlineActions != null ? itemActions.slice(0, maxInlineActions) : itemActions).map((action, i) => /* @__PURE__ */ jsxRuntime.jsxs(
29675
- exports.Button,
29676
- {
29677
- variant: action.variant ?? "ghost",
29678
- size: "sm",
29679
- onClick: handleActionClick(action, row),
29680
- "data-testid": `action-${action.event}`,
29681
- "data-row-id": String(row.id),
29682
- className: cn(action.variant === "danger" && "text-error hover:bg-error/10"),
29683
- children: [
29684
- action.icon && renderIconInput3(action.icon, { size: "xs", className: "mr-1" }),
29685
- action.label
29686
- ]
29687
- },
29688
- i
29689
- )),
29690
- maxInlineActions != null && itemActions.length > maxInlineActions && /* @__PURE__ */ jsxRuntime.jsx(
29691
- exports.Menu,
29692
- {
29693
- position: "bottom-end",
29694
- trigger: /* @__PURE__ */ jsxRuntime.jsx(exports.Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: "more-horizontal", size: "xs" }) }),
29695
- items: itemActions.slice(maxInlineActions).map((action) => ({
29696
- label: action.label,
29697
- icon: action.icon,
29698
- event: action.event,
29699
- variant: action.variant === "danger" ? "danger" : "default",
29700
- onClick: () => eventBus.emit(`UI:${action.event}`, {
29701
- id: row.id,
29702
- row
29703
- })
29704
- }))
29705
- }
29706
- )
29707
- ] })
29686
+ itemActions && itemActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
29687
+ exports.HStack,
29688
+ {
29689
+ gap: "xs",
29690
+ className: cn(
29691
+ // Pinned: the fixed column tracks routinely overflow the caller's
29692
+ // scroll container, which used to leave the actions off-screen.
29693
+ // Opaque so scrolled cells pass underneath it.
29694
+ "justify-end flex-shrink-0 sticky right-0 z-[1] transition-colors",
29695
+ lk.striped && index % 2 === 1 ? "bg-[var(--color-surface-subtle)]" : "bg-[var(--color-card)] group-hover:bg-[var(--color-surface-subtle)]"
29696
+ ),
29697
+ children: [
29698
+ (maxInlineActions != null ? itemActions.slice(0, maxInlineActions) : itemActions).map((action, i) => /* @__PURE__ */ jsxRuntime.jsxs(
29699
+ exports.Button,
29700
+ {
29701
+ variant: action.variant ?? "ghost",
29702
+ size: "sm",
29703
+ onClick: handleActionClick(action, row),
29704
+ "data-testid": `action-${action.event}`,
29705
+ "data-row-id": String(row.id),
29706
+ className: cn(action.variant === "danger" && "text-error hover:bg-error/10"),
29707
+ children: [
29708
+ action.icon && renderIconInput3(action.icon, { size: "xs", className: "mr-1" }),
29709
+ action.label
29710
+ ]
29711
+ },
29712
+ i
29713
+ )),
29714
+ maxInlineActions != null && itemActions.length > maxInlineActions && /* @__PURE__ */ jsxRuntime.jsx(
29715
+ exports.Menu,
29716
+ {
29717
+ position: "bottom-end",
29718
+ trigger: /* @__PURE__ */ jsxRuntime.jsx(exports.Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: "more-horizontal", size: "xs" }) }),
29719
+ items: itemActions.slice(maxInlineActions).map((action) => ({
29720
+ label: action.label,
29721
+ icon: action.icon,
29722
+ event: action.event,
29723
+ variant: action.variant === "danger" ? "danger" : "default",
29724
+ onClick: () => eventBus.emit(`UI:${action.event}`, {
29725
+ id: row.id,
29726
+ row
29727
+ })
29728
+ }))
29729
+ }
29730
+ )
29731
+ ]
29732
+ }
29733
+ )
29708
29734
  ]
29709
29735
  }
29710
29736
  );
@@ -38657,6 +38683,16 @@ function normalizeFieldDefs(fields) {
38657
38683
  return String(f3);
38658
38684
  });
38659
38685
  }
38686
+ function buildFieldLabelMap(fields) {
38687
+ const map = {};
38688
+ if (!fields) return map;
38689
+ for (const f3 of fields) {
38690
+ if (typeof f3 === "object" && "key" in f3 && f3.header !== void 0 && f3.header !== "") {
38691
+ map[f3.key] = f3.header;
38692
+ }
38693
+ }
38694
+ return map;
38695
+ }
38660
38696
  function buildFieldTypeMap(fields) {
38661
38697
  const map = {};
38662
38698
  if (!fields) return map;
@@ -38708,6 +38744,8 @@ var init_DetailPanel = __esm({
38708
38744
  };
38709
38745
  const effectiveFieldNames = isFieldDefArray(propFields) ? normalizeFieldDefs(propFields) : fieldNames;
38710
38746
  const fieldTypeMap = isFieldDefArray(propFields) ? buildFieldTypeMap(propFields) : {};
38747
+ const fieldLabelMap = isFieldDefArray(propFields) ? buildFieldLabelMap(propFields) : {};
38748
+ const labelFor = (field) => fieldLabelMap[field] ?? formatFieldLabel(field);
38711
38749
  const handleActionClick = React74.useCallback(
38712
38750
  (action, data2) => {
38713
38751
  if (action.navigatesTo) {
@@ -38743,7 +38781,7 @@ var init_DetailPanel = __esm({
38743
38781
  if (typeof field === "string") {
38744
38782
  const value = getNestedValue(normalizedData, field);
38745
38783
  return {
38746
- label: formatFieldLabel(field),
38784
+ label: labelFor(field),
38747
38785
  value: formatFieldValue(value, field),
38748
38786
  icon: getFieldIcon(field)
38749
38787
  };
@@ -38754,7 +38792,10 @@ var init_DetailPanel = __esm({
38754
38792
  }
38755
38793
  if (normalizedData && effectiveFieldNames) {
38756
38794
  const primaryField = effectiveFieldNames[0];
38757
- if (!title && primaryField && normalizedData[primaryField]) {
38795
+ const titleDerivedFromPrimary = Boolean(
38796
+ !title && primaryField && normalizedData[primaryField]
38797
+ );
38798
+ if (titleDerivedFromPrimary) {
38758
38799
  title = String(normalizedData[primaryField]);
38759
38800
  }
38760
38801
  const statusFields = effectiveFieldNames.filter(
@@ -38773,16 +38814,16 @@ var init_DetailPanel = __esm({
38773
38814
  (f3) => f3.toLowerCase().includes("description") || f3.toLowerCase().includes("note")
38774
38815
  );
38775
38816
  const otherFields = effectiveFieldNames.filter(
38776
- (f3) => f3 !== primaryField && !statusFields.includes(f3) && !progressFields.includes(f3) && !metricFields.includes(f3) && !dateFields.includes(f3) && !descriptionFields.includes(f3)
38817
+ (f3) => (!titleDerivedFromPrimary || f3 !== primaryField) && !statusFields.includes(f3) && !progressFields.includes(f3) && !metricFields.includes(f3) && !dateFields.includes(f3) && !descriptionFields.includes(f3)
38777
38818
  );
38778
38819
  sections = [];
38779
38820
  if (statusFields.length > 0 || otherFields.length > 0) {
38780
38821
  const overviewFields = [];
38781
- [...statusFields, ...otherFields.slice(0, 3)].forEach((field) => {
38822
+ [...statusFields, ...otherFields].forEach((field) => {
38782
38823
  const value = getNestedValue(normalizedData, field);
38783
38824
  if (value !== void 0 && value !== null) {
38784
38825
  overviewFields.push({
38785
- label: formatFieldLabel(field),
38826
+ label: labelFor(field),
38786
38827
  value: renderRichFieldValue(value, field, fieldTypeMap[field]),
38787
38828
  icon: getFieldIcon(field)
38788
38829
  });
@@ -38798,7 +38839,7 @@ var init_DetailPanel = __esm({
38798
38839
  const value = getNestedValue(normalizedData, field);
38799
38840
  if (value !== void 0 && value !== null) {
38800
38841
  metricsFields.push({
38801
- label: formatFieldLabel(field),
38842
+ label: labelFor(field),
38802
38843
  value: renderRichFieldValue(value, field, fieldTypeMap[field]),
38803
38844
  icon: getFieldIcon(field)
38804
38845
  });
@@ -38814,7 +38855,7 @@ var init_DetailPanel = __esm({
38814
38855
  const value = getNestedValue(normalizedData, field);
38815
38856
  if (value !== void 0 && value !== null) {
38816
38857
  timelineFields.push({
38817
- label: formatFieldLabel(field),
38858
+ label: labelFor(field),
38818
38859
  value: renderRichFieldValue(value, field, fieldTypeMap[field]),
38819
38860
  icon: getFieldIcon(field)
38820
38861
  });
@@ -38830,7 +38871,7 @@ var init_DetailPanel = __esm({
38830
38871
  const value = getNestedValue(normalizedData, field);
38831
38872
  if (value !== void 0 && value !== null) {
38832
38873
  descFields.push({
38833
- label: formatFieldLabel(field),
38874
+ label: labelFor(field),
38834
38875
  value: renderRichFieldValue(value, field, fieldTypeMap[field]),
38835
38876
  icon: getFieldIcon(field)
38836
38877
  });
@@ -38878,7 +38919,7 @@ var init_DetailPanel = __esm({
38878
38919
  if (typeof field === "string") {
38879
38920
  const value = normalizedData ? getNestedValue(normalizedData, field) : void 0;
38880
38921
  allFields.push({
38881
- label: formatFieldLabel(field),
38922
+ label: labelFor(field),
38882
38923
  value: renderRichFieldValue(value, field, fieldTypeMap[field]),
38883
38924
  icon: getFieldIcon(field)
38884
38925
  });
@@ -45381,6 +45422,13 @@ function MaybeTraitScope({
45381
45422
  if (wrap) {
45382
45423
  return /* @__PURE__ */ jsxRuntime.jsx(providers.TraitScopeProvider, { orbital, trait: sourceTrait, children });
45383
45424
  }
45425
+ if (sourceTrait !== void 0 && schemaCtx !== null) {
45426
+ scopeWrapLog.warn("decline", {
45427
+ sourceTrait,
45428
+ orbitalsByTraitSize: schemaCtx.orbitalsByTrait.size,
45429
+ reason: "sourceTrait not in orbitalsByTrait \u2014 children render unscoped"
45430
+ });
45431
+ }
45384
45432
  return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
45385
45433
  }
45386
45434
  function UISlotComponent({
@@ -45678,7 +45726,7 @@ function renderPatternChildren(children, onDismiss, parentId = "root", parentPat
45678
45726
  const childId = `${parentId}-${index}`;
45679
45727
  const childPath = parentPath === "root" ? `root.children.${index}` : `${parentPath}.children.${index}`;
45680
45728
  const childAsRecord = child;
45681
- const { type: _childType, props: nestedProps, _id: _childNodeId, children: _childChildren, ...flatProps } = childAsRecord;
45729
+ const { type: _childType, props: nestedProps, _id: _childNodeId, _sourceTrait: childSourceTrait, children: _childChildren, ...flatProps } = childAsRecord;
45682
45730
  const resolvedProps = nestedProps !== void 0 ? nestedProps : flatProps;
45683
45731
  if (_childChildren !== void 0 && nestedProps === void 0) {
45684
45732
  resolvedProps.children = _childChildren;
@@ -45693,11 +45741,14 @@ function renderPatternChildren(children, onDismiss, parentId = "root", parentPat
45693
45741
  // (e.g. form-section inside a stack) can resolve entityDef via
45694
45742
  // the trait's linkedEntity for form-field enrichment. The orbCtx
45695
45743
  // (slot/transition/state/entity) propagates the same way so every
45696
- // nested node carries a complete contextual-edit address.
45697
- ...sourceTrait !== void 0 && { sourceTrait },
45744
+ // nested node carries a complete contextual-edit address. A child
45745
+ // carrying its own `_sourceTrait` (multi-source slot stack) owns it
45746
+ // outright — inheriting the synthetic wrapper's sentinel instead
45747
+ // would erase the real owner.
45748
+ ...childSourceTrait !== void 0 ? { sourceTrait: childSourceTrait } : sourceTrait !== void 0 && { sourceTrait },
45698
45749
  ...orbCtx ?? {}
45699
45750
  };
45700
- return /* @__PURE__ */ jsxRuntime.jsx(
45751
+ const renderedChild = /* @__PURE__ */ jsxRuntime.jsx(
45701
45752
  SlotContentRenderer,
45702
45753
  {
45703
45754
  content: childContent,
@@ -45706,6 +45757,7 @@ function renderPatternChildren(children, onDismiss, parentId = "root", parentPat
45706
45757
  },
45707
45758
  childId
45708
45759
  );
45760
+ return childSourceTrait !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(MaybeTraitScope, { sourceTrait: childSourceTrait, children: renderedChild }, childId) : renderedChild;
45709
45761
  });
45710
45762
  }
45711
45763
  function toDrawableNodes(children) {
@@ -47254,9 +47306,10 @@ function aggregateSlot(sources) {
47254
47306
  const entries = Object.entries(sources);
47255
47307
  if (entries.length === 0) return null;
47256
47308
  if (entries.length === 1) return entries[0][1];
47257
- const children = entries.map(([, entry]) => ({
47309
+ const children = entries.map(([sourceKey, entry]) => ({
47258
47310
  type: entry.pattern,
47259
- ...entry.props
47311
+ ...entry.props,
47312
+ ...sourceKey !== DEFAULT_SOURCE_KEY && { _sourceTrait: sourceKey }
47260
47313
  }));
47261
47314
  const stackId = `slot-content-stack-${entries.map(([k]) => k).join("-")}`;
47262
47315
  return {