@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.
@@ -21255,17 +21255,27 @@ function getWeekDays(start) {
21255
21255
  }
21256
21256
  return days;
21257
21257
  }
21258
- function generateDefaultTimeSlots() {
21258
+ function generateDefaultTimeSlots(events2) {
21259
+ let first = DEFAULT_FIRST_HOUR;
21260
+ let last = DEFAULT_LAST_HOUR;
21261
+ for (const ev of events2) {
21262
+ const start = new Date(ev.startTime);
21263
+ if (Number.isNaN(start.getTime())) continue;
21264
+ const hour = start.getHours();
21265
+ if (hour < first) first = hour;
21266
+ if (hour > last) last = hour;
21267
+ }
21259
21268
  const slots = [];
21260
- for (let hour = 9; hour <= 17; hour++) {
21261
- slots.push(`${hour.toString().padStart(2, "0")}:00`);
21269
+ for (let hour = first; hour <= last; hour++) {
21270
+ slots.push(slotLabel(hour));
21262
21271
  }
21263
21272
  return slots;
21264
21273
  }
21265
21274
  function eventInSlot(event, day, slotTime) {
21266
21275
  const eventStart = new Date(event.startTime);
21267
- const [slotHour, slotMinute] = slotTime.split(":").map(Number);
21268
- return eventStart.toDateString() === day.toDateString() && eventStart.getHours() === slotHour && eventStart.getMinutes() === slotMinute;
21276
+ if (Number.isNaN(eventStart.getTime())) return false;
21277
+ const [slotHour] = slotTime.split(":").map(Number);
21278
+ return eventStart.toDateString() === day.toDateString() && eventStart.getHours() === slotHour;
21269
21279
  }
21270
21280
  function CalendarGrid({
21271
21281
  weekStart,
@@ -21294,8 +21304,8 @@ function CalendarGrid({
21294
21304
  [resolvedWeekStart]
21295
21305
  );
21296
21306
  const resolvedTimeSlots = React91.useMemo(
21297
- () => timeSlots ?? generateDefaultTimeSlots(),
21298
- [timeSlots]
21307
+ () => timeSlots ?? generateDefaultTimeSlots(evs),
21308
+ [timeSlots, evs]
21299
21309
  );
21300
21310
  const visibleCount = useDayWindow(dayWindow);
21301
21311
  const [dayOffset, setDayOffset] = React91.useState(0);
@@ -21482,7 +21492,7 @@ function CalendarGrid({
21482
21492
  }
21483
21493
  );
21484
21494
  }
21485
- var SHORT_DATE;
21495
+ var SHORT_DATE, DEFAULT_FIRST_HOUR, DEFAULT_LAST_HOUR, slotLabel;
21486
21496
  var init_CalendarGrid = __esm({
21487
21497
  "components/core/molecules/CalendarGrid.tsx"() {
21488
21498
  "use client";
@@ -21497,6 +21507,9 @@ var init_CalendarGrid = __esm({
21497
21507
  init_useEventBus();
21498
21508
  init_useSwipeGesture();
21499
21509
  SHORT_DATE = { month: "short", day: "numeric" };
21510
+ DEFAULT_FIRST_HOUR = 9;
21511
+ DEFAULT_LAST_HOUR = 17;
21512
+ slotLabel = (hour) => `${hour.toString().padStart(2, "0")}:00`;
21500
21513
  CalendarGrid.displayName = "CalendarGrid";
21501
21514
  }
21502
21515
  });
@@ -25581,13 +25594,13 @@ function DataGrid({
25581
25594
  }
25582
25595
  ),
25583
25596
  /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", className: "flex-1 min-w-0", children: [
25584
- titleValue !== void 0 && titleValue !== null && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", className: "items-center", children: [
25597
+ titleValue !== void 0 && titleValue !== null && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", className: "items-center min-w-0", children: [
25585
25598
  titleField?.icon && renderIconInput(titleField.icon, { size: "sm", className: "text-primary flex-shrink-0" }),
25586
25599
  /* @__PURE__ */ jsxRuntime.jsx(
25587
25600
  Typography,
25588
25601
  {
25589
25602
  variant: titleField?.variant === "h3" ? "h3" : "h4",
25590
- className: "font-semibold truncate",
25603
+ className: "font-semibold truncate min-w-0",
25591
25604
  children: String(titleValue)
25592
25605
  }
25593
25606
  )
@@ -25645,7 +25658,7 @@ function DataGrid({
25645
25658
  )
25646
25659
  ] }, field.name);
25647
25660
  }) }) }),
25648
- 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: [
25661
+ 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: [
25649
25662
  (maxInlineActions != null ? primaryActions.slice(0, maxInlineActions) : primaryActions).map((action, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
25650
25663
  Button,
25651
25664
  {
@@ -30658,7 +30671,7 @@ function TableView({
30658
30671
  col.key
30659
30672
  );
30660
30673
  }),
30661
- hasActions && /* @__PURE__ */ jsxRuntime.jsx(Box, { "aria-hidden": true })
30674
+ hasActions && /* @__PURE__ */ jsxRuntime.jsx(Box, { "aria-hidden": true, className: "sticky right-0 bg-[var(--color-surface-subtle)]" })
30662
30675
  ]
30663
30676
  }
30664
30677
  );
@@ -30702,41 +30715,54 @@ function TableView({
30702
30715
  }
30703
30716
  return /* @__PURE__ */ jsxRuntime.jsx(Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: formatCell(raw, col.format) }) }, col.key);
30704
30717
  }),
30705
- 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: [
30706
- (maxInlineActions != null ? itemActions.slice(0, maxInlineActions) : itemActions).map((action, i) => /* @__PURE__ */ jsxRuntime.jsxs(
30707
- Button,
30708
- {
30709
- variant: action.variant ?? "ghost",
30710
- size: "sm",
30711
- onClick: handleActionClick(action, row),
30712
- "data-testid": `action-${action.event}`,
30713
- "data-row-id": String(row.id),
30714
- className: cn(action.variant === "danger" && "text-error hover:bg-error/10"),
30715
- children: [
30716
- action.icon && renderIconInput3(action.icon, { size: "xs", className: "mr-1" }),
30717
- action.label
30718
- ]
30719
- },
30720
- i
30721
- )),
30722
- maxInlineActions != null && itemActions.length > maxInlineActions && /* @__PURE__ */ jsxRuntime.jsx(
30723
- Menu,
30724
- {
30725
- position: "bottom-end",
30726
- 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" }) }),
30727
- items: itemActions.slice(maxInlineActions).map((action) => ({
30728
- label: action.label,
30729
- icon: action.icon,
30730
- event: action.event,
30731
- variant: action.variant === "danger" ? "danger" : "default",
30732
- onClick: () => eventBus.emit(`UI:${action.event}`, {
30733
- id: row.id,
30734
- row
30735
- })
30736
- }))
30737
- }
30738
- )
30739
- ] })
30718
+ itemActions && itemActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
30719
+ HStack,
30720
+ {
30721
+ gap: "xs",
30722
+ className: cn(
30723
+ // Pinned: the fixed column tracks routinely overflow the caller's
30724
+ // scroll container, which used to leave the actions off-screen.
30725
+ // Opaque so scrolled cells pass underneath it.
30726
+ "justify-end flex-shrink-0 sticky right-0 z-[1] transition-colors",
30727
+ lk.striped && index % 2 === 1 ? "bg-[var(--color-surface-subtle)]" : "bg-[var(--color-card)] group-hover:bg-[var(--color-surface-subtle)]"
30728
+ ),
30729
+ children: [
30730
+ (maxInlineActions != null ? itemActions.slice(0, maxInlineActions) : itemActions).map((action, i) => /* @__PURE__ */ jsxRuntime.jsxs(
30731
+ Button,
30732
+ {
30733
+ variant: action.variant ?? "ghost",
30734
+ size: "sm",
30735
+ onClick: handleActionClick(action, row),
30736
+ "data-testid": `action-${action.event}`,
30737
+ "data-row-id": String(row.id),
30738
+ className: cn(action.variant === "danger" && "text-error hover:bg-error/10"),
30739
+ children: [
30740
+ action.icon && renderIconInput3(action.icon, { size: "xs", className: "mr-1" }),
30741
+ action.label
30742
+ ]
30743
+ },
30744
+ i
30745
+ )),
30746
+ maxInlineActions != null && itemActions.length > maxInlineActions && /* @__PURE__ */ jsxRuntime.jsx(
30747
+ Menu,
30748
+ {
30749
+ position: "bottom-end",
30750
+ 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" }) }),
30751
+ items: itemActions.slice(maxInlineActions).map((action) => ({
30752
+ label: action.label,
30753
+ icon: action.icon,
30754
+ event: action.event,
30755
+ variant: action.variant === "danger" ? "danger" : "default",
30756
+ onClick: () => eventBus.emit(`UI:${action.event}`, {
30757
+ id: row.id,
30758
+ row
30759
+ })
30760
+ }))
30761
+ }
30762
+ )
30763
+ ]
30764
+ }
30765
+ )
30740
30766
  ]
30741
30767
  }
30742
30768
  );
@@ -39062,6 +39088,16 @@ function normalizeFieldDefs(fields) {
39062
39088
  return String(f3);
39063
39089
  });
39064
39090
  }
39091
+ function buildFieldLabelMap(fields) {
39092
+ const map = {};
39093
+ if (!fields) return map;
39094
+ for (const f3 of fields) {
39095
+ if (typeof f3 === "object" && "key" in f3 && f3.header !== void 0 && f3.header !== "") {
39096
+ map[f3.key] = f3.header;
39097
+ }
39098
+ }
39099
+ return map;
39100
+ }
39065
39101
  function buildFieldTypeMap(fields) {
39066
39102
  const map = {};
39067
39103
  if (!fields) return map;
@@ -39113,6 +39149,8 @@ var init_DetailPanel = __esm({
39113
39149
  };
39114
39150
  const effectiveFieldNames = isFieldDefArray(propFields) ? normalizeFieldDefs(propFields) : fieldNames;
39115
39151
  const fieldTypeMap = isFieldDefArray(propFields) ? buildFieldTypeMap(propFields) : {};
39152
+ const fieldLabelMap = isFieldDefArray(propFields) ? buildFieldLabelMap(propFields) : {};
39153
+ const labelFor = (field) => fieldLabelMap[field] ?? formatFieldLabel(field);
39116
39154
  const handleActionClick = React91.useCallback(
39117
39155
  (action, data2) => {
39118
39156
  if (action.navigatesTo) {
@@ -39148,7 +39186,7 @@ var init_DetailPanel = __esm({
39148
39186
  if (typeof field === "string") {
39149
39187
  const value = getNestedValue(normalizedData, field);
39150
39188
  return {
39151
- label: formatFieldLabel(field),
39189
+ label: labelFor(field),
39152
39190
  value: formatFieldValue(value, field),
39153
39191
  icon: getFieldIcon(field)
39154
39192
  };
@@ -39159,7 +39197,10 @@ var init_DetailPanel = __esm({
39159
39197
  }
39160
39198
  if (normalizedData && effectiveFieldNames) {
39161
39199
  const primaryField = effectiveFieldNames[0];
39162
- if (!title && primaryField && normalizedData[primaryField]) {
39200
+ const titleDerivedFromPrimary = Boolean(
39201
+ !title && primaryField && normalizedData[primaryField]
39202
+ );
39203
+ if (titleDerivedFromPrimary) {
39163
39204
  title = String(normalizedData[primaryField]);
39164
39205
  }
39165
39206
  const statusFields = effectiveFieldNames.filter(
@@ -39178,16 +39219,16 @@ var init_DetailPanel = __esm({
39178
39219
  (f3) => f3.toLowerCase().includes("description") || f3.toLowerCase().includes("note")
39179
39220
  );
39180
39221
  const otherFields = effectiveFieldNames.filter(
39181
- (f3) => f3 !== primaryField && !statusFields.includes(f3) && !progressFields.includes(f3) && !metricFields.includes(f3) && !dateFields.includes(f3) && !descriptionFields.includes(f3)
39222
+ (f3) => (!titleDerivedFromPrimary || f3 !== primaryField) && !statusFields.includes(f3) && !progressFields.includes(f3) && !metricFields.includes(f3) && !dateFields.includes(f3) && !descriptionFields.includes(f3)
39182
39223
  );
39183
39224
  sections = [];
39184
39225
  if (statusFields.length > 0 || otherFields.length > 0) {
39185
39226
  const overviewFields = [];
39186
- [...statusFields, ...otherFields.slice(0, 3)].forEach((field) => {
39227
+ [...statusFields, ...otherFields].forEach((field) => {
39187
39228
  const value = getNestedValue(normalizedData, field);
39188
39229
  if (value !== void 0 && value !== null) {
39189
39230
  overviewFields.push({
39190
- label: formatFieldLabel(field),
39231
+ label: labelFor(field),
39191
39232
  value: renderRichFieldValue(value, field, fieldTypeMap[field]),
39192
39233
  icon: getFieldIcon(field)
39193
39234
  });
@@ -39203,7 +39244,7 @@ var init_DetailPanel = __esm({
39203
39244
  const value = getNestedValue(normalizedData, field);
39204
39245
  if (value !== void 0 && value !== null) {
39205
39246
  metricsFields.push({
39206
- label: formatFieldLabel(field),
39247
+ label: labelFor(field),
39207
39248
  value: renderRichFieldValue(value, field, fieldTypeMap[field]),
39208
39249
  icon: getFieldIcon(field)
39209
39250
  });
@@ -39219,7 +39260,7 @@ var init_DetailPanel = __esm({
39219
39260
  const value = getNestedValue(normalizedData, field);
39220
39261
  if (value !== void 0 && value !== null) {
39221
39262
  timelineFields.push({
39222
- label: formatFieldLabel(field),
39263
+ label: labelFor(field),
39223
39264
  value: renderRichFieldValue(value, field, fieldTypeMap[field]),
39224
39265
  icon: getFieldIcon(field)
39225
39266
  });
@@ -39235,7 +39276,7 @@ var init_DetailPanel = __esm({
39235
39276
  const value = getNestedValue(normalizedData, field);
39236
39277
  if (value !== void 0 && value !== null) {
39237
39278
  descFields.push({
39238
- label: formatFieldLabel(field),
39279
+ label: labelFor(field),
39239
39280
  value: renderRichFieldValue(value, field, fieldTypeMap[field]),
39240
39281
  icon: getFieldIcon(field)
39241
39282
  });
@@ -39283,7 +39324,7 @@ var init_DetailPanel = __esm({
39283
39324
  if (typeof field === "string") {
39284
39325
  const value = normalizedData ? getNestedValue(normalizedData, field) : void 0;
39285
39326
  allFields.push({
39286
- label: formatFieldLabel(field),
39327
+ label: labelFor(field),
39287
39328
  value: renderRichFieldValue(value, field, fieldTypeMap[field]),
39288
39329
  icon: getFieldIcon(field)
39289
39330
  });
@@ -45805,6 +45846,13 @@ function MaybeTraitScope({
45805
45846
  if (wrap) {
45806
45847
  return /* @__PURE__ */ jsxRuntime.jsx(providers.TraitScopeProvider, { orbital, trait: sourceTrait, children });
45807
45848
  }
45849
+ if (sourceTrait !== void 0 && schemaCtx !== null) {
45850
+ scopeWrapLog.warn("decline", {
45851
+ sourceTrait,
45852
+ orbitalsByTraitSize: schemaCtx.orbitalsByTrait.size,
45853
+ reason: "sourceTrait not in orbitalsByTrait \u2014 children render unscoped"
45854
+ });
45855
+ }
45808
45856
  return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
45809
45857
  }
45810
45858
  function UISlotComponent({
@@ -46102,7 +46150,7 @@ function renderPatternChildren(children, onDismiss, parentId = "root", parentPat
46102
46150
  const childId = `${parentId}-${index}`;
46103
46151
  const childPath = parentPath === "root" ? `root.children.${index}` : `${parentPath}.children.${index}`;
46104
46152
  const childAsRecord = child;
46105
- const { type: _childType, props: nestedProps, _id: _childNodeId, children: _childChildren, ...flatProps } = childAsRecord;
46153
+ const { type: _childType, props: nestedProps, _id: _childNodeId, _sourceTrait: childSourceTrait, children: _childChildren, ...flatProps } = childAsRecord;
46106
46154
  const resolvedProps = nestedProps !== void 0 ? nestedProps : flatProps;
46107
46155
  if (_childChildren !== void 0 && nestedProps === void 0) {
46108
46156
  resolvedProps.children = _childChildren;
@@ -46117,11 +46165,14 @@ function renderPatternChildren(children, onDismiss, parentId = "root", parentPat
46117
46165
  // (e.g. form-section inside a stack) can resolve entityDef via
46118
46166
  // the trait's linkedEntity for form-field enrichment. The orbCtx
46119
46167
  // (slot/transition/state/entity) propagates the same way so every
46120
- // nested node carries a complete contextual-edit address.
46121
- ...sourceTrait !== void 0 && { sourceTrait },
46168
+ // nested node carries a complete contextual-edit address. A child
46169
+ // carrying its own `_sourceTrait` (multi-source slot stack) owns it
46170
+ // outright — inheriting the synthetic wrapper's sentinel instead
46171
+ // would erase the real owner.
46172
+ ...childSourceTrait !== void 0 ? { sourceTrait: childSourceTrait } : sourceTrait !== void 0 && { sourceTrait },
46122
46173
  ...orbCtx ?? {}
46123
46174
  };
46124
- return /* @__PURE__ */ jsxRuntime.jsx(
46175
+ const renderedChild = /* @__PURE__ */ jsxRuntime.jsx(
46125
46176
  SlotContentRenderer,
46126
46177
  {
46127
46178
  content: childContent,
@@ -46130,6 +46181,7 @@ function renderPatternChildren(children, onDismiss, parentId = "root", parentPat
46130
46181
  },
46131
46182
  childId
46132
46183
  );
46184
+ return childSourceTrait !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(MaybeTraitScope, { sourceTrait: childSourceTrait, children: renderedChild }, childId) : renderedChild;
46133
46185
  });
46134
46186
  }
46135
46187
  function toDrawableNodes(children) {
@@ -51686,11 +51738,9 @@ function OrbPreview({
51686
51738
  }, [parsedSchema]);
51687
51739
  const initialPageMatch = React91.useMemo(() => {
51688
51740
  if (!initialPagePath) return void 0;
51689
- for (const { page } of pages) {
51690
- const params = page.path ? providers.matchPath(page.path, initialPagePath) : null;
51691
- if (params !== null) return { name: page.name, params };
51692
- }
51693
- return void 0;
51741
+ const hit = providers.matchPathAmong(pages, initialPagePath, (entry) => entry.page.path);
51742
+ if (!hit) return void 0;
51743
+ return { name: hit.candidate.page.name, params: hit.params };
51694
51744
  }, [pages, initialPagePath]);
51695
51745
  const initialPageName = initialPageMatch?.name;
51696
51746
  const [currentPage, setCurrentPage] = React91.useState(initialPageName);
@@ -51704,16 +51754,9 @@ function OrbPreview({
51704
51754
  }
51705
51755
  }, [initialPagePath, initialPageName, initialPageMatch]);
51706
51756
  const handleNavigate = React91.useCallback((path) => {
51707
- let match;
51708
- let params = {};
51709
- for (const entry of pages) {
51710
- const m = entry.page.path ? providers.matchPath(entry.page.path, path) : null;
51711
- if (m !== null) {
51712
- match = entry;
51713
- params = m;
51714
- break;
51715
- }
51716
- }
51757
+ const hit = providers.matchPathAmong(pages, path, (entry) => entry.page.path);
51758
+ const match = hit?.candidate;
51759
+ const params = hit?.params ?? {};
51717
51760
  navLog.debug("handleNavigate", () => ({
51718
51761
  path,
51719
51762
  matched: match?.page.name ?? null,