@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.
package/dist/avl/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
2
  import * as React91 from 'react';
3
3
  import React91__default, { createContext, useState, useMemo, useRef, useEffect, useContext, useCallback, Suspense, useLayoutEffect, Profiler, useReducer, useSyncExternalStore, lazy, useId } from 'react';
4
- import { getAllPages, matchPath, OrbitalProvider, EventBusContext, useTraitScopeChain, ServerBridgeProvider, VerificationProvider, EntitySchemaProvider, OrbitalThemeProvider, useServerBridge, useEntitySchema, useEntitySchemaOptional, TraitScopeProvider, useCurrentPagePath, useGameAudioContextOptional } from '@almadar/ui/providers';
4
+ import { getAllPages, matchPathAmong, OrbitalProvider, EventBusContext, useTraitScopeChain, ServerBridgeProvider, VerificationProvider, EntitySchemaProvider, OrbitalThemeProvider, useServerBridge, useEntitySchema, useEntitySchemaOptional, TraitScopeProvider, useCurrentPagePath, useGameAudioContextOptional } from '@almadar/ui/providers';
5
5
  import { createLogger, setNamespaceLevel, isLogLevelEnabled } from '@almadar/logger';
6
6
  import ELK from 'elkjs/lib/elk.bundled.js';
7
7
  import { MarkerType, useReactFlow, Handle, Position, getBezierPath, EdgeLabelRenderer, useNodeId, ReactFlowProvider, BaseEdge, useNodesState, useEdgesState, ReactFlow, Controls, Background, BackgroundVariant } from '@xyflow/react';
@@ -21179,17 +21179,27 @@ function getWeekDays(start) {
21179
21179
  }
21180
21180
  return days;
21181
21181
  }
21182
- function generateDefaultTimeSlots() {
21182
+ function generateDefaultTimeSlots(events2) {
21183
+ let first = DEFAULT_FIRST_HOUR;
21184
+ let last = DEFAULT_LAST_HOUR;
21185
+ for (const ev of events2) {
21186
+ const start = new Date(ev.startTime);
21187
+ if (Number.isNaN(start.getTime())) continue;
21188
+ const hour = start.getHours();
21189
+ if (hour < first) first = hour;
21190
+ if (hour > last) last = hour;
21191
+ }
21183
21192
  const slots = [];
21184
- for (let hour = 9; hour <= 17; hour++) {
21185
- slots.push(`${hour.toString().padStart(2, "0")}:00`);
21193
+ for (let hour = first; hour <= last; hour++) {
21194
+ slots.push(slotLabel(hour));
21186
21195
  }
21187
21196
  return slots;
21188
21197
  }
21189
21198
  function eventInSlot(event, day, slotTime) {
21190
21199
  const eventStart = new Date(event.startTime);
21191
- const [slotHour, slotMinute] = slotTime.split(":").map(Number);
21192
- return eventStart.toDateString() === day.toDateString() && eventStart.getHours() === slotHour && eventStart.getMinutes() === slotMinute;
21200
+ if (Number.isNaN(eventStart.getTime())) return false;
21201
+ const [slotHour] = slotTime.split(":").map(Number);
21202
+ return eventStart.toDateString() === day.toDateString() && eventStart.getHours() === slotHour;
21193
21203
  }
21194
21204
  function CalendarGrid({
21195
21205
  weekStart,
@@ -21218,8 +21228,8 @@ function CalendarGrid({
21218
21228
  [resolvedWeekStart]
21219
21229
  );
21220
21230
  const resolvedTimeSlots = useMemo(
21221
- () => timeSlots ?? generateDefaultTimeSlots(),
21222
- [timeSlots]
21231
+ () => timeSlots ?? generateDefaultTimeSlots(evs),
21232
+ [timeSlots, evs]
21223
21233
  );
21224
21234
  const visibleCount = useDayWindow(dayWindow);
21225
21235
  const [dayOffset, setDayOffset] = useState(0);
@@ -21406,7 +21416,7 @@ function CalendarGrid({
21406
21416
  }
21407
21417
  );
21408
21418
  }
21409
- var SHORT_DATE;
21419
+ var SHORT_DATE, DEFAULT_FIRST_HOUR, DEFAULT_LAST_HOUR, slotLabel;
21410
21420
  var init_CalendarGrid = __esm({
21411
21421
  "components/core/molecules/CalendarGrid.tsx"() {
21412
21422
  "use client";
@@ -21421,6 +21431,9 @@ var init_CalendarGrid = __esm({
21421
21431
  init_useEventBus();
21422
21432
  init_useSwipeGesture();
21423
21433
  SHORT_DATE = { month: "short", day: "numeric" };
21434
+ DEFAULT_FIRST_HOUR = 9;
21435
+ DEFAULT_LAST_HOUR = 17;
21436
+ slotLabel = (hour) => `${hour.toString().padStart(2, "0")}:00`;
21424
21437
  CalendarGrid.displayName = "CalendarGrid";
21425
21438
  }
21426
21439
  });
@@ -25505,13 +25518,13 @@ function DataGrid({
25505
25518
  }
25506
25519
  ),
25507
25520
  /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: "flex-1 min-w-0", children: [
25508
- titleValue !== void 0 && titleValue !== null && /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center", children: [
25521
+ titleValue !== void 0 && titleValue !== null && /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center min-w-0", children: [
25509
25522
  titleField?.icon && renderIconInput(titleField.icon, { size: "sm", className: "text-primary flex-shrink-0" }),
25510
25523
  /* @__PURE__ */ jsx(
25511
25524
  Typography,
25512
25525
  {
25513
25526
  variant: titleField?.variant === "h3" ? "h3" : "h4",
25514
- className: "font-semibold truncate",
25527
+ className: "font-semibold truncate min-w-0",
25515
25528
  children: String(titleValue)
25516
25529
  }
25517
25530
  )
@@ -25569,7 +25582,7 @@ function DataGrid({
25569
25582
  )
25570
25583
  ] }, field.name);
25571
25584
  }) }) }),
25572
- 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: [
25585
+ 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: [
25573
25586
  (maxInlineActions != null ? primaryActions.slice(0, maxInlineActions) : primaryActions).map((action, idx) => /* @__PURE__ */ jsxs(
25574
25587
  Button,
25575
25588
  {
@@ -30582,7 +30595,7 @@ function TableView({
30582
30595
  col.key
30583
30596
  );
30584
30597
  }),
30585
- hasActions && /* @__PURE__ */ jsx(Box, { "aria-hidden": true })
30598
+ hasActions && /* @__PURE__ */ jsx(Box, { "aria-hidden": true, className: "sticky right-0 bg-[var(--color-surface-subtle)]" })
30586
30599
  ]
30587
30600
  }
30588
30601
  );
@@ -30626,41 +30639,54 @@ function TableView({
30626
30639
  }
30627
30640
  return /* @__PURE__ */ jsx(Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsx("span", { className: "truncate", children: formatCell(raw, col.format) }) }, col.key);
30628
30641
  }),
30629
- itemActions && itemActions.length > 0 && /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "justify-end flex-shrink-0 opacity-60 group-hover:opacity-100 transition-opacity", children: [
30630
- (maxInlineActions != null ? itemActions.slice(0, maxInlineActions) : itemActions).map((action, i) => /* @__PURE__ */ jsxs(
30631
- Button,
30632
- {
30633
- variant: action.variant ?? "ghost",
30634
- size: "sm",
30635
- onClick: handleActionClick(action, row),
30636
- "data-testid": `action-${action.event}`,
30637
- "data-row-id": String(row.id),
30638
- className: cn(action.variant === "danger" && "text-error hover:bg-error/10"),
30639
- children: [
30640
- action.icon && renderIconInput3(action.icon, { size: "xs", className: "mr-1" }),
30641
- action.label
30642
- ]
30643
- },
30644
- i
30645
- )),
30646
- maxInlineActions != null && itemActions.length > maxInlineActions && /* @__PURE__ */ jsx(
30647
- Menu,
30648
- {
30649
- position: "bottom-end",
30650
- 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" }) }),
30651
- items: itemActions.slice(maxInlineActions).map((action) => ({
30652
- label: action.label,
30653
- icon: action.icon,
30654
- event: action.event,
30655
- variant: action.variant === "danger" ? "danger" : "default",
30656
- onClick: () => eventBus.emit(`UI:${action.event}`, {
30657
- id: row.id,
30658
- row
30659
- })
30660
- }))
30661
- }
30662
- )
30663
- ] })
30642
+ itemActions && itemActions.length > 0 && /* @__PURE__ */ jsxs(
30643
+ HStack,
30644
+ {
30645
+ gap: "xs",
30646
+ className: cn(
30647
+ // Pinned: the fixed column tracks routinely overflow the caller's
30648
+ // scroll container, which used to leave the actions off-screen.
30649
+ // Opaque so scrolled cells pass underneath it.
30650
+ "justify-end flex-shrink-0 sticky right-0 z-[1] transition-colors",
30651
+ lk.striped && index % 2 === 1 ? "bg-[var(--color-surface-subtle)]" : "bg-[var(--color-card)] group-hover:bg-[var(--color-surface-subtle)]"
30652
+ ),
30653
+ children: [
30654
+ (maxInlineActions != null ? itemActions.slice(0, maxInlineActions) : itemActions).map((action, i) => /* @__PURE__ */ jsxs(
30655
+ Button,
30656
+ {
30657
+ variant: action.variant ?? "ghost",
30658
+ size: "sm",
30659
+ onClick: handleActionClick(action, row),
30660
+ "data-testid": `action-${action.event}`,
30661
+ "data-row-id": String(row.id),
30662
+ className: cn(action.variant === "danger" && "text-error hover:bg-error/10"),
30663
+ children: [
30664
+ action.icon && renderIconInput3(action.icon, { size: "xs", className: "mr-1" }),
30665
+ action.label
30666
+ ]
30667
+ },
30668
+ i
30669
+ )),
30670
+ maxInlineActions != null && itemActions.length > maxInlineActions && /* @__PURE__ */ jsx(
30671
+ Menu,
30672
+ {
30673
+ position: "bottom-end",
30674
+ 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" }) }),
30675
+ items: itemActions.slice(maxInlineActions).map((action) => ({
30676
+ label: action.label,
30677
+ icon: action.icon,
30678
+ event: action.event,
30679
+ variant: action.variant === "danger" ? "danger" : "default",
30680
+ onClick: () => eventBus.emit(`UI:${action.event}`, {
30681
+ id: row.id,
30682
+ row
30683
+ })
30684
+ }))
30685
+ }
30686
+ )
30687
+ ]
30688
+ }
30689
+ )
30664
30690
  ]
30665
30691
  }
30666
30692
  );
@@ -38986,6 +39012,16 @@ function normalizeFieldDefs(fields) {
38986
39012
  return String(f3);
38987
39013
  });
38988
39014
  }
39015
+ function buildFieldLabelMap(fields) {
39016
+ const map = {};
39017
+ if (!fields) return map;
39018
+ for (const f3 of fields) {
39019
+ if (typeof f3 === "object" && "key" in f3 && f3.header !== void 0 && f3.header !== "") {
39020
+ map[f3.key] = f3.header;
39021
+ }
39022
+ }
39023
+ return map;
39024
+ }
38989
39025
  function buildFieldTypeMap(fields) {
38990
39026
  const map = {};
38991
39027
  if (!fields) return map;
@@ -39037,6 +39073,8 @@ var init_DetailPanel = __esm({
39037
39073
  };
39038
39074
  const effectiveFieldNames = isFieldDefArray(propFields) ? normalizeFieldDefs(propFields) : fieldNames;
39039
39075
  const fieldTypeMap = isFieldDefArray(propFields) ? buildFieldTypeMap(propFields) : {};
39076
+ const fieldLabelMap = isFieldDefArray(propFields) ? buildFieldLabelMap(propFields) : {};
39077
+ const labelFor = (field) => fieldLabelMap[field] ?? formatFieldLabel(field);
39040
39078
  const handleActionClick = useCallback(
39041
39079
  (action, data2) => {
39042
39080
  if (action.navigatesTo) {
@@ -39072,7 +39110,7 @@ var init_DetailPanel = __esm({
39072
39110
  if (typeof field === "string") {
39073
39111
  const value = getNestedValue(normalizedData, field);
39074
39112
  return {
39075
- label: formatFieldLabel(field),
39113
+ label: labelFor(field),
39076
39114
  value: formatFieldValue(value, field),
39077
39115
  icon: getFieldIcon(field)
39078
39116
  };
@@ -39083,7 +39121,10 @@ var init_DetailPanel = __esm({
39083
39121
  }
39084
39122
  if (normalizedData && effectiveFieldNames) {
39085
39123
  const primaryField = effectiveFieldNames[0];
39086
- if (!title && primaryField && normalizedData[primaryField]) {
39124
+ const titleDerivedFromPrimary = Boolean(
39125
+ !title && primaryField && normalizedData[primaryField]
39126
+ );
39127
+ if (titleDerivedFromPrimary) {
39087
39128
  title = String(normalizedData[primaryField]);
39088
39129
  }
39089
39130
  const statusFields = effectiveFieldNames.filter(
@@ -39102,16 +39143,16 @@ var init_DetailPanel = __esm({
39102
39143
  (f3) => f3.toLowerCase().includes("description") || f3.toLowerCase().includes("note")
39103
39144
  );
39104
39145
  const otherFields = effectiveFieldNames.filter(
39105
- (f3) => f3 !== primaryField && !statusFields.includes(f3) && !progressFields.includes(f3) && !metricFields.includes(f3) && !dateFields.includes(f3) && !descriptionFields.includes(f3)
39146
+ (f3) => (!titleDerivedFromPrimary || f3 !== primaryField) && !statusFields.includes(f3) && !progressFields.includes(f3) && !metricFields.includes(f3) && !dateFields.includes(f3) && !descriptionFields.includes(f3)
39106
39147
  );
39107
39148
  sections = [];
39108
39149
  if (statusFields.length > 0 || otherFields.length > 0) {
39109
39150
  const overviewFields = [];
39110
- [...statusFields, ...otherFields.slice(0, 3)].forEach((field) => {
39151
+ [...statusFields, ...otherFields].forEach((field) => {
39111
39152
  const value = getNestedValue(normalizedData, field);
39112
39153
  if (value !== void 0 && value !== null) {
39113
39154
  overviewFields.push({
39114
- label: formatFieldLabel(field),
39155
+ label: labelFor(field),
39115
39156
  value: renderRichFieldValue(value, field, fieldTypeMap[field]),
39116
39157
  icon: getFieldIcon(field)
39117
39158
  });
@@ -39127,7 +39168,7 @@ var init_DetailPanel = __esm({
39127
39168
  const value = getNestedValue(normalizedData, field);
39128
39169
  if (value !== void 0 && value !== null) {
39129
39170
  metricsFields.push({
39130
- label: formatFieldLabel(field),
39171
+ label: labelFor(field),
39131
39172
  value: renderRichFieldValue(value, field, fieldTypeMap[field]),
39132
39173
  icon: getFieldIcon(field)
39133
39174
  });
@@ -39143,7 +39184,7 @@ var init_DetailPanel = __esm({
39143
39184
  const value = getNestedValue(normalizedData, field);
39144
39185
  if (value !== void 0 && value !== null) {
39145
39186
  timelineFields.push({
39146
- label: formatFieldLabel(field),
39187
+ label: labelFor(field),
39147
39188
  value: renderRichFieldValue(value, field, fieldTypeMap[field]),
39148
39189
  icon: getFieldIcon(field)
39149
39190
  });
@@ -39159,7 +39200,7 @@ var init_DetailPanel = __esm({
39159
39200
  const value = getNestedValue(normalizedData, field);
39160
39201
  if (value !== void 0 && value !== null) {
39161
39202
  descFields.push({
39162
- label: formatFieldLabel(field),
39203
+ label: labelFor(field),
39163
39204
  value: renderRichFieldValue(value, field, fieldTypeMap[field]),
39164
39205
  icon: getFieldIcon(field)
39165
39206
  });
@@ -39207,7 +39248,7 @@ var init_DetailPanel = __esm({
39207
39248
  if (typeof field === "string") {
39208
39249
  const value = normalizedData ? getNestedValue(normalizedData, field) : void 0;
39209
39250
  allFields.push({
39210
- label: formatFieldLabel(field),
39251
+ label: labelFor(field),
39211
39252
  value: renderRichFieldValue(value, field, fieldTypeMap[field]),
39212
39253
  icon: getFieldIcon(field)
39213
39254
  });
@@ -45729,6 +45770,13 @@ function MaybeTraitScope({
45729
45770
  if (wrap) {
45730
45771
  return /* @__PURE__ */ jsx(TraitScopeProvider, { orbital, trait: sourceTrait, children });
45731
45772
  }
45773
+ if (sourceTrait !== void 0 && schemaCtx !== null) {
45774
+ scopeWrapLog.warn("decline", {
45775
+ sourceTrait,
45776
+ orbitalsByTraitSize: schemaCtx.orbitalsByTrait.size,
45777
+ reason: "sourceTrait not in orbitalsByTrait \u2014 children render unscoped"
45778
+ });
45779
+ }
45732
45780
  return /* @__PURE__ */ jsx(Fragment, { children });
45733
45781
  }
45734
45782
  function UISlotComponent({
@@ -46026,7 +46074,7 @@ function renderPatternChildren(children, onDismiss, parentId = "root", parentPat
46026
46074
  const childId = `${parentId}-${index}`;
46027
46075
  const childPath = parentPath === "root" ? `root.children.${index}` : `${parentPath}.children.${index}`;
46028
46076
  const childAsRecord = child;
46029
- const { type: _childType, props: nestedProps, _id: _childNodeId, children: _childChildren, ...flatProps } = childAsRecord;
46077
+ const { type: _childType, props: nestedProps, _id: _childNodeId, _sourceTrait: childSourceTrait, children: _childChildren, ...flatProps } = childAsRecord;
46030
46078
  const resolvedProps = nestedProps !== void 0 ? nestedProps : flatProps;
46031
46079
  if (_childChildren !== void 0 && nestedProps === void 0) {
46032
46080
  resolvedProps.children = _childChildren;
@@ -46041,11 +46089,14 @@ function renderPatternChildren(children, onDismiss, parentId = "root", parentPat
46041
46089
  // (e.g. form-section inside a stack) can resolve entityDef via
46042
46090
  // the trait's linkedEntity for form-field enrichment. The orbCtx
46043
46091
  // (slot/transition/state/entity) propagates the same way so every
46044
- // nested node carries a complete contextual-edit address.
46045
- ...sourceTrait !== void 0 && { sourceTrait },
46092
+ // nested node carries a complete contextual-edit address. A child
46093
+ // carrying its own `_sourceTrait` (multi-source slot stack) owns it
46094
+ // outright — inheriting the synthetic wrapper's sentinel instead
46095
+ // would erase the real owner.
46096
+ ...childSourceTrait !== void 0 ? { sourceTrait: childSourceTrait } : sourceTrait !== void 0 && { sourceTrait },
46046
46097
  ...orbCtx ?? {}
46047
46098
  };
46048
- return /* @__PURE__ */ jsx(
46099
+ const renderedChild = /* @__PURE__ */ jsx(
46049
46100
  SlotContentRenderer,
46050
46101
  {
46051
46102
  content: childContent,
@@ -46054,6 +46105,7 @@ function renderPatternChildren(children, onDismiss, parentId = "root", parentPat
46054
46105
  },
46055
46106
  childId
46056
46107
  );
46108
+ return childSourceTrait !== void 0 ? /* @__PURE__ */ jsx(MaybeTraitScope, { sourceTrait: childSourceTrait, children: renderedChild }, childId) : renderedChild;
46057
46109
  });
46058
46110
  }
46059
46111
  function toDrawableNodes(children) {
@@ -51610,11 +51662,9 @@ function OrbPreview({
51610
51662
  }, [parsedSchema]);
51611
51663
  const initialPageMatch = useMemo(() => {
51612
51664
  if (!initialPagePath) return void 0;
51613
- for (const { page } of pages) {
51614
- const params = page.path ? matchPath(page.path, initialPagePath) : null;
51615
- if (params !== null) return { name: page.name, params };
51616
- }
51617
- return void 0;
51665
+ const hit = matchPathAmong(pages, initialPagePath, (entry) => entry.page.path);
51666
+ if (!hit) return void 0;
51667
+ return { name: hit.candidate.page.name, params: hit.params };
51618
51668
  }, [pages, initialPagePath]);
51619
51669
  const initialPageName = initialPageMatch?.name;
51620
51670
  const [currentPage, setCurrentPage] = useState(initialPageName);
@@ -51628,16 +51678,9 @@ function OrbPreview({
51628
51678
  }
51629
51679
  }, [initialPagePath, initialPageName, initialPageMatch]);
51630
51680
  const handleNavigate = useCallback((path) => {
51631
- let match;
51632
- let params = {};
51633
- for (const entry of pages) {
51634
- const m = entry.page.path ? matchPath(entry.page.path, path) : null;
51635
- if (m !== null) {
51636
- match = entry;
51637
- params = m;
51638
- break;
51639
- }
51640
- }
51681
+ const hit = matchPathAmong(pages, path, (entry) => entry.page.path);
51682
+ const match = hit?.candidate;
51683
+ const params = hit?.params ?? {};
51641
51684
  navLog.debug("handleNavigate", () => ({
51642
51685
  path,
51643
51686
  matched: match?.page.name ?? null,