@almadar/ui 5.122.13 → 5.123.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.
@@ -62,6 +62,7 @@ var langPowershell = require('react-syntax-highlighter/dist/esm/languages/prism/
62
62
  var langMakefile = require('react-syntax-highlighter/dist/esm/languages/prism/makefile.js');
63
63
  var langNginx = require('react-syntax-highlighter/dist/esm/languages/prism/nginx.js');
64
64
  var langIni = require('react-syntax-highlighter/dist/esm/languages/prism/ini.js');
65
+ var langClike = require('react-syntax-highlighter/dist/esm/languages/prism/clike.js');
65
66
  var ReactMarkdown = require('react-markdown');
66
67
  var remarkGfm = require('remark-gfm');
67
68
  var remarkMath = require('remark-math');
@@ -147,6 +148,7 @@ var langPowershell__default = /*#__PURE__*/_interopDefault(langPowershell);
147
148
  var langMakefile__default = /*#__PURE__*/_interopDefault(langMakefile);
148
149
  var langNginx__default = /*#__PURE__*/_interopDefault(langNginx);
149
150
  var langIni__default = /*#__PURE__*/_interopDefault(langIni);
151
+ var langClike__default = /*#__PURE__*/_interopDefault(langClike);
150
152
  var ReactMarkdown__default = /*#__PURE__*/_interopDefault(ReactMarkdown);
151
153
  var remarkGfm__default = /*#__PURE__*/_interopDefault(remarkGfm);
152
154
  var remarkMath__default = /*#__PURE__*/_interopDefault(remarkMath);
@@ -1640,6 +1642,7 @@ var init_Button = __esm({
1640
1642
  "button",
1641
1643
  {
1642
1644
  ref,
1645
+ type: "button",
1643
1646
  disabled: disabled || isLoading,
1644
1647
  className: cn(
1645
1648
  "relative inline-flex items-center justify-center gap-2",
@@ -1733,6 +1736,11 @@ var init_Typography = __esm({
1733
1736
  inherit: "text-inherit"
1734
1737
  };
1735
1738
  weightStyles = {
1739
+ // Neutral: an atom default of "none" must not override the variant's baked
1740
+ // weight (C-PATTERN-ENUM-BLOCKS-NEUTRAL-OVERRIDE — `variant={h2}` rendered
1741
+ // tiny because the ui-typography atom's `weight: light`/`size: xs` defaults
1742
+ // clobbered every variant).
1743
+ none: "",
1736
1744
  light: "font-light",
1737
1745
  normal: "font-normal",
1738
1746
  medium: "font-medium",
@@ -1758,6 +1766,7 @@ var init_Typography = __esm({
1758
1766
  label: "span"
1759
1767
  };
1760
1768
  typographySizeStyles = {
1769
+ none: "",
1761
1770
  xs: "text-xs",
1762
1771
  sm: "text-sm",
1763
1772
  md: "text-base",
@@ -14164,7 +14173,7 @@ async function loadPrismLanguage(lang) {
14164
14173
  if (isLanguageRegistered(lang)) return;
14165
14174
  try {
14166
14175
  const grammar = codeLanguageLoader ? await codeLanguageLoader(lang) : null;
14167
- if (grammar) SyntaxHighlighter__default.default.registerLanguage(lang, grammar);
14176
+ SyntaxHighlighter__default.default.registerLanguage(lang, grammar ?? langClike__default.default);
14168
14177
  dynamicallyLoaded.add(lang);
14169
14178
  } catch {
14170
14179
  dynamicallyLoaded.add(lang);
@@ -14333,6 +14342,7 @@ var init_CodeBlock = __esm({
14333
14342
  SyntaxHighlighter__default.default.registerLanguage("make", langMakefile__default.default);
14334
14343
  SyntaxHighlighter__default.default.registerLanguage("nginx", langNginx__default.default);
14335
14344
  SyntaxHighlighter__default.default.registerLanguage("ini", langIni__default.default);
14345
+ SyntaxHighlighter__default.default.registerLanguage("clike", langClike__default.default);
14336
14346
  SyntaxHighlighter__default.default.registerLanguage("orb", syntax.orbLanguage);
14337
14347
  SyntaxHighlighter__default.default.registerLanguage("lolo", syntax.loloLanguage);
14338
14348
  dynamicallyLoaded = /* @__PURE__ */ new Set();
@@ -14882,7 +14892,7 @@ var init_CodeBlock = __esm({
14882
14892
  ] }) });
14883
14893
  }
14884
14894
  const hasHeader = showLanguageBadge || effectiveCopy;
14885
- return /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: `relative group ${className || ""}`, style: { display: "flex", flexDirection: "column", height: "100%" }, children: [
14895
+ return /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: `relative group not-prose ${className || ""}`, style: { display: "flex", flexDirection: "column", height: "100%" }, children: [
14886
14896
  hasHeader && /* @__PURE__ */ jsxRuntime.jsxs(
14887
14897
  HStack,
14888
14898
  {
@@ -22694,6 +22704,13 @@ function DataGrid({
22694
22704
  row: itemData
22695
22705
  };
22696
22706
  eventBus.emit(`UI:${action.event}`, payload);
22707
+ if (action.navigatesTo) {
22708
+ const url = action.navigatesTo.replace(
22709
+ /\{\{row\.(\w+(?:\.\w+)*)\}\}/g,
22710
+ (_, field) => String(itemData[field] ?? "")
22711
+ );
22712
+ eventBus.emit("UI:NAVIGATE", { url });
22713
+ }
22697
22714
  };
22698
22715
  const hasRenderProp = typeof children === "function";
22699
22716
  React82.useEffect(() => {
@@ -22761,13 +22778,49 @@ function DataGrid({
22761
22778
  const wrapDnd = (node) => dnd.isZone ? /* @__PURE__ */ jsxRuntime.jsx(dnd.SortableItem, { id: dndId, children: node }, dndId) : node;
22762
22779
  if (hasRenderProp) {
22763
22780
  return wrapDnd(
22764
- /* @__PURE__ */ jsxRuntime.jsx(
22781
+ /* @__PURE__ */ jsxRuntime.jsxs(
22765
22782
  Box,
22766
22783
  {
22767
22784
  "data-entity-row": true,
22768
22785
  "data-entity-id": id,
22769
22786
  className: cn(isSelected && "ring-2 ring-primary rounded-lg"),
22770
- children: children(itemData, index)
22787
+ children: [
22788
+ children(itemData, index),
22789
+ actionDefs.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "px-4 py-3 border-t border-border", children: /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", className: "justify-end", children: [
22790
+ (maxInlineActions != null ? actionDefs.slice(0, maxInlineActions) : actionDefs).map((action, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
22791
+ Button,
22792
+ {
22793
+ variant: action.variant === "primary" ? "primary" : "ghost",
22794
+ size: "sm",
22795
+ onClick: handleActionClick(action, itemData),
22796
+ "data-testid": `action-${action.event}`,
22797
+ "data-row-id": String(itemData.id),
22798
+ className: action.variant === "danger" ? "text-error hover:text-error hover:bg-error/10" : void 0,
22799
+ children: [
22800
+ action.icon && renderIconInput(action.icon, { size: "xs", className: "mr-1" }),
22801
+ action.label
22802
+ ]
22803
+ },
22804
+ idx
22805
+ )),
22806
+ maxInlineActions != null && actionDefs.length > maxInlineActions && /* @__PURE__ */ jsxRuntime.jsx(
22807
+ Menu,
22808
+ {
22809
+ position: "bottom-end",
22810
+ 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" }) }),
22811
+ items: actionDefs.slice(maxInlineActions).map((action) => ({
22812
+ label: action.label,
22813
+ icon: action.icon,
22814
+ event: action.event,
22815
+ onClick: () => eventBus.emit(`UI:${action.event}`, {
22816
+ id: itemData.id,
22817
+ row: itemData
22818
+ })
22819
+ }))
22820
+ }
22821
+ )
22822
+ ] }) })
22823
+ ]
22771
22824
  },
22772
22825
  id
22773
22826
  )
@@ -23727,7 +23780,9 @@ var init_FilterGroup = __esm({
23727
23780
  showIcon = true,
23728
23781
  query,
23729
23782
  isLoading,
23730
- look = "toolbar"
23783
+ look = "toolbar",
23784
+ event,
23785
+ clearEvent
23731
23786
  }) => {
23732
23787
  const { t } = hooks.useTranslate();
23733
23788
  const eventBus = useEventBus();
@@ -23764,14 +23819,18 @@ var init_FilterGroup = __esm({
23764
23819
  queryState.setFilter(field, value === "all" ? null : value);
23765
23820
  }
23766
23821
  onFilterChange?.(field, value === "all" ? null : value);
23767
- eventBus.emit("UI:FILTER", {
23822
+ const payload = {
23768
23823
  entity,
23769
23824
  field,
23770
23825
  value: value === "all" || value === null ? "" : value,
23771
23826
  query
23772
- });
23827
+ };
23828
+ if (event && event !== "FILTER") {
23829
+ eventBus.emit(`UI:${event}`, payload);
23830
+ }
23831
+ eventBus.emit("UI:FILTER", payload);
23773
23832
  },
23774
- [onFilterChange, queryState, eventBus, entity, query]
23833
+ [onFilterChange, queryState, eventBus, entity, query, event]
23775
23834
  );
23776
23835
  const handleClearAll = React82.useCallback(() => {
23777
23836
  setSelectedValues({});
@@ -23779,8 +23838,11 @@ var init_FilterGroup = __esm({
23779
23838
  queryState.clearFilters();
23780
23839
  }
23781
23840
  onClearAll?.();
23841
+ if (clearEvent && clearEvent !== "CLEAR_FILTERS") {
23842
+ eventBus.emit(`UI:${clearEvent}`, { entity, query });
23843
+ }
23782
23844
  eventBus.emit("UI:CLEAR_FILTERS", { entity, query });
23783
- }, [onClearAll, queryState, eventBus, entity, query]);
23845
+ }, [onClearAll, queryState, eventBus, entity, query, clearEvent]);
23784
23846
  const activeFilterCount = Object.keys(selectedValues).length;
23785
23847
  if (variant === "pills") {
23786
23848
  return /* @__PURE__ */ jsxRuntime.jsxs(
@@ -24908,7 +24970,7 @@ var init_SearchInput = __esm({
24908
24970
  queryState.setSearch(newValue);
24909
24971
  }
24910
24972
  onSearch?.(newValue);
24911
- if (event) {
24973
+ if (event && event !== "SEARCH") {
24912
24974
  eventBus.emit(`UI:${event}`, { searchTerm: newValue, entity });
24913
24975
  }
24914
24976
  if (event || entity || query) {
@@ -44324,6 +44386,14 @@ function createSharedEntityWriter(binding, tick, traitStatesRef, emit) {
44324
44386
  return writes;
44325
44387
  };
44326
44388
  }
44389
+ function containsConfigForward(value) {
44390
+ if (typeof value === "string") return value.startsWith("@config.");
44391
+ if (Array.isArray(value)) return value.some(containsConfigForward);
44392
+ if (value !== null && typeof value === "object") {
44393
+ return Object.values(value).some(containsConfigForward);
44394
+ }
44395
+ return false;
44396
+ }
44327
44397
  function getBindingConfig(binding) {
44328
44398
  const raw = binding.config;
44329
44399
  const normalized = runtime.normalizeCallSiteConfigToValues(raw);
@@ -44676,7 +44746,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
44676
44746
  const sharedCallSite = sharedCallSiteRaw ? runtime.resolveCallSitePayloadCaptures(
44677
44747
  Object.fromEntries(
44678
44748
  Object.entries(sharedCallSiteRaw).filter(
44679
- ([, v]) => !(typeof v === "string" && v.startsWith("@config."))
44749
+ ([, v]) => !containsConfigForward(v)
44680
44750
  )
44681
44751
  ),
44682
44752
  payload || {}
@@ -44741,7 +44811,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
44741
44811
  const callSiteOverrides = callSiteConfig ? runtime.resolveCallSitePayloadCaptures(
44742
44812
  Object.fromEntries(
44743
44813
  Object.entries(callSiteConfig).filter(
44744
- ([, v]) => !(typeof v === "string" && v.startsWith("@config."))
44814
+ ([, v]) => !containsConfigForward(v)
44745
44815
  )
44746
44816
  ),
44747
44817
  payload || {}
@@ -44983,7 +45053,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
44983
45053
  }
44984
45054
  return () => scheduler.stopAll();
44985
45055
  }, [traitBindings, runTickEffects, sharedGroups, sharedEntityStore, emitFromSharedWriter]);
44986
- const processEventQueued = React82.useCallback(async (eventKey, payload) => {
45056
+ const processEventQueued = React82.useCallback(async (eventKey, payload, targetTrait) => {
44987
45057
  const normalizedEvent = normalizeEventKey(eventKey);
44988
45058
  const bindings = traitBindingsRef.current;
44989
45059
  const currentManager = managerRef.current;
@@ -45012,7 +45082,9 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
45012
45082
  normalizedEvent,
45013
45083
  payload,
45014
45084
  void 0,
45015
- entityByTrait
45085
+ entityByTrait,
45086
+ void 0,
45087
+ targetTrait
45016
45088
  );
45017
45089
  crossTraitLog.debug("processEvent:results", {
45018
45090
  event: normalizedEvent,
@@ -45105,8 +45177,11 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
45105
45177
  const effectTraces = result.effects.map(
45106
45178
  (e) => {
45107
45179
  if (Array.isArray(e)) {
45180
+ const head = String(e[0] ?? "unknown");
45181
+ const entityName = head === "fetch" && typeof e[1] === "string" ? e[1] : head === "persist" && typeof e[2] === "string" ? e[2] : void 0;
45108
45182
  return {
45109
- type: String(e[0] ?? "unknown"),
45183
+ type: head,
45184
+ ...entityName !== void 0 ? { entityName } : {},
45110
45185
  args: e.slice(1),
45111
45186
  status: "executed"
45112
45187
  };
@@ -45159,7 +45234,8 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
45159
45234
  const orbital = orbitalsByTrait?.[traitName];
45160
45235
  if (orbital) dispatchedOrbitals.add(orbital);
45161
45236
  }
45162
- await onEventProcessed(normalizedEvent, payload, dispatchedOrbitals);
45237
+ const relayPayload = targetTrait !== void 0 ? { ...payload ?? {}, _targetTrait: targetTrait } : payload;
45238
+ await onEventProcessed(normalizedEvent, relayPayload, dispatchedOrbitals);
45163
45239
  }
45164
45240
  }, [entities, eventBus, sharedEntityStore]);
45165
45241
  const drainEventQueue = React82.useCallback(async () => {
@@ -45168,14 +45244,14 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
45168
45244
  try {
45169
45245
  while (eventQueueRef.current.length > 0) {
45170
45246
  const entry = eventQueueRef.current.shift();
45171
- await processEventQueued(entry.eventKey, entry.payload);
45247
+ await processEventQueued(entry.eventKey, entry.payload, entry.targetTrait);
45172
45248
  }
45173
45249
  } finally {
45174
45250
  processingRef.current = false;
45175
45251
  }
45176
45252
  }, [processEventQueued]);
45177
- const enqueueAndDrain = React82.useCallback((eventKey, payload) => {
45178
- eventQueueRef.current.push({ eventKey, payload });
45253
+ const enqueueAndDrain = React82.useCallback((eventKey, payload, targetTrait) => {
45254
+ eventQueueRef.current.push({ eventKey, payload, targetTrait });
45179
45255
  void drainEventQueue();
45180
45256
  }, [drainEventQueue]);
45181
45257
  React82.useCallback((eventKey, payload) => {
@@ -45272,7 +45348,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
45272
45348
  crossTraitLog.debug("listen:subscribed", { busKey, targetTrait: binding.trait.name, sourceOrbital, sourceTrait, listenEvent: listen.event, triggers: listen.triggers });
45273
45349
  const unsub = eventBus.on(busKey, (event) => {
45274
45350
  crossTraitLog.debug("listen:fired", { busKey, targetTrait: binding.trait.name, triggers: listen.triggers });
45275
- enqueueAndDrain(listen.triggers, event.payload);
45351
+ enqueueAndDrain(listen.triggers, event.payload, binding.trait.name);
45276
45352
  });
45277
45353
  unsubscribes.push(() => {
45278
45354
  crossTraitLog.debug("listen:unsubscribe", { busKey, targetTrait: binding.trait.name, triggers: listen.triggers });
@@ -45840,6 +45916,15 @@ function OrbPreview({
45840
45916
  }
45841
45917
  }
45842
45918
  }, [pages]);
45919
+ React82.useEffect(() => {
45920
+ const unsubscribe = eventBus.on("UI:NAVIGATE", (event) => {
45921
+ const url = event.payload?.url;
45922
+ if (typeof url === "string" && url.length > 0) {
45923
+ handleNavigate(url);
45924
+ }
45925
+ });
45926
+ return unsubscribe;
45927
+ }, [eventBus, handleNavigate]);
45843
45928
  if (!parseResult.ok) {
45844
45929
  return /* @__PURE__ */ jsxRuntime.jsx(Box, { className, style: { height }, children: /* @__PURE__ */ jsxRuntime.jsxs(Typography, { as: "pre", color: "error", variant: "small", className: "font-mono whitespace-pre-wrap break-all m-0 p-4", children: [
45845
45930
  "Parse error: ",
@@ -64,6 +64,7 @@ import langPowershell from 'react-syntax-highlighter/dist/esm/languages/prism/po
64
64
  import langMakefile from 'react-syntax-highlighter/dist/esm/languages/prism/makefile.js';
65
65
  import langNginx from 'react-syntax-highlighter/dist/esm/languages/prism/nginx.js';
66
66
  import langIni from 'react-syntax-highlighter/dist/esm/languages/prism/ini.js';
67
+ import langClike from 'react-syntax-highlighter/dist/esm/languages/prism/clike.js';
67
68
  import ReactMarkdown from 'react-markdown';
68
69
  import remarkGfm from 'remark-gfm';
69
70
  import remarkMath from 'remark-math';
@@ -1567,6 +1568,7 @@ var init_Button = __esm({
1567
1568
  "button",
1568
1569
  {
1569
1570
  ref,
1571
+ type: "button",
1570
1572
  disabled: disabled || isLoading,
1571
1573
  className: cn(
1572
1574
  "relative inline-flex items-center justify-center gap-2",
@@ -1660,6 +1662,11 @@ var init_Typography = __esm({
1660
1662
  inherit: "text-inherit"
1661
1663
  };
1662
1664
  weightStyles = {
1665
+ // Neutral: an atom default of "none" must not override the variant's baked
1666
+ // weight (C-PATTERN-ENUM-BLOCKS-NEUTRAL-OVERRIDE — `variant={h2}` rendered
1667
+ // tiny because the ui-typography atom's `weight: light`/`size: xs` defaults
1668
+ // clobbered every variant).
1669
+ none: "",
1663
1670
  light: "font-light",
1664
1671
  normal: "font-normal",
1665
1672
  medium: "font-medium",
@@ -1685,6 +1692,7 @@ var init_Typography = __esm({
1685
1692
  label: "span"
1686
1693
  };
1687
1694
  typographySizeStyles = {
1695
+ none: "",
1688
1696
  xs: "text-xs",
1689
1697
  sm: "text-sm",
1690
1698
  md: "text-base",
@@ -14091,7 +14099,7 @@ async function loadPrismLanguage(lang) {
14091
14099
  if (isLanguageRegistered(lang)) return;
14092
14100
  try {
14093
14101
  const grammar = codeLanguageLoader ? await codeLanguageLoader(lang) : null;
14094
- if (grammar) SyntaxHighlighter.registerLanguage(lang, grammar);
14102
+ SyntaxHighlighter.registerLanguage(lang, grammar ?? langClike);
14095
14103
  dynamicallyLoaded.add(lang);
14096
14104
  } catch {
14097
14105
  dynamicallyLoaded.add(lang);
@@ -14260,6 +14268,7 @@ var init_CodeBlock = __esm({
14260
14268
  SyntaxHighlighter.registerLanguage("make", langMakefile);
14261
14269
  SyntaxHighlighter.registerLanguage("nginx", langNginx);
14262
14270
  SyntaxHighlighter.registerLanguage("ini", langIni);
14271
+ SyntaxHighlighter.registerLanguage("clike", langClike);
14263
14272
  SyntaxHighlighter.registerLanguage("orb", orbLanguage);
14264
14273
  SyntaxHighlighter.registerLanguage("lolo", loloLanguage);
14265
14274
  dynamicallyLoaded = /* @__PURE__ */ new Set();
@@ -14809,7 +14818,7 @@ var init_CodeBlock = __esm({
14809
14818
  ] }) });
14810
14819
  }
14811
14820
  const hasHeader = showLanguageBadge || effectiveCopy;
14812
- return /* @__PURE__ */ jsxs(Box, { className: `relative group ${className || ""}`, style: { display: "flex", flexDirection: "column", height: "100%" }, children: [
14821
+ return /* @__PURE__ */ jsxs(Box, { className: `relative group not-prose ${className || ""}`, style: { display: "flex", flexDirection: "column", height: "100%" }, children: [
14813
14822
  hasHeader && /* @__PURE__ */ jsxs(
14814
14823
  HStack,
14815
14824
  {
@@ -22621,6 +22630,13 @@ function DataGrid({
22621
22630
  row: itemData
22622
22631
  };
22623
22632
  eventBus.emit(`UI:${action.event}`, payload);
22633
+ if (action.navigatesTo) {
22634
+ const url = action.navigatesTo.replace(
22635
+ /\{\{row\.(\w+(?:\.\w+)*)\}\}/g,
22636
+ (_, field) => String(itemData[field] ?? "")
22637
+ );
22638
+ eventBus.emit("UI:NAVIGATE", { url });
22639
+ }
22624
22640
  };
22625
22641
  const hasRenderProp = typeof children === "function";
22626
22642
  useEffect(() => {
@@ -22688,13 +22704,49 @@ function DataGrid({
22688
22704
  const wrapDnd = (node) => dnd.isZone ? /* @__PURE__ */ jsx(dnd.SortableItem, { id: dndId, children: node }, dndId) : node;
22689
22705
  if (hasRenderProp) {
22690
22706
  return wrapDnd(
22691
- /* @__PURE__ */ jsx(
22707
+ /* @__PURE__ */ jsxs(
22692
22708
  Box,
22693
22709
  {
22694
22710
  "data-entity-row": true,
22695
22711
  "data-entity-id": id,
22696
22712
  className: cn(isSelected && "ring-2 ring-primary rounded-lg"),
22697
- children: children(itemData, index)
22713
+ children: [
22714
+ children(itemData, index),
22715
+ actionDefs.length > 0 && /* @__PURE__ */ jsx(Box, { className: "px-4 py-3 border-t border-border", children: /* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "justify-end", children: [
22716
+ (maxInlineActions != null ? actionDefs.slice(0, maxInlineActions) : actionDefs).map((action, idx) => /* @__PURE__ */ jsxs(
22717
+ Button,
22718
+ {
22719
+ variant: action.variant === "primary" ? "primary" : "ghost",
22720
+ size: "sm",
22721
+ onClick: handleActionClick(action, itemData),
22722
+ "data-testid": `action-${action.event}`,
22723
+ "data-row-id": String(itemData.id),
22724
+ className: action.variant === "danger" ? "text-error hover:text-error hover:bg-error/10" : void 0,
22725
+ children: [
22726
+ action.icon && renderIconInput(action.icon, { size: "xs", className: "mr-1" }),
22727
+ action.label
22728
+ ]
22729
+ },
22730
+ idx
22731
+ )),
22732
+ maxInlineActions != null && actionDefs.length > maxInlineActions && /* @__PURE__ */ jsx(
22733
+ Menu,
22734
+ {
22735
+ position: "bottom-end",
22736
+ 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" }) }),
22737
+ items: actionDefs.slice(maxInlineActions).map((action) => ({
22738
+ label: action.label,
22739
+ icon: action.icon,
22740
+ event: action.event,
22741
+ onClick: () => eventBus.emit(`UI:${action.event}`, {
22742
+ id: itemData.id,
22743
+ row: itemData
22744
+ })
22745
+ }))
22746
+ }
22747
+ )
22748
+ ] }) })
22749
+ ]
22698
22750
  },
22699
22751
  id
22700
22752
  )
@@ -23654,7 +23706,9 @@ var init_FilterGroup = __esm({
23654
23706
  showIcon = true,
23655
23707
  query,
23656
23708
  isLoading,
23657
- look = "toolbar"
23709
+ look = "toolbar",
23710
+ event,
23711
+ clearEvent
23658
23712
  }) => {
23659
23713
  const { t } = useTranslate();
23660
23714
  const eventBus = useEventBus();
@@ -23691,14 +23745,18 @@ var init_FilterGroup = __esm({
23691
23745
  queryState.setFilter(field, value === "all" ? null : value);
23692
23746
  }
23693
23747
  onFilterChange?.(field, value === "all" ? null : value);
23694
- eventBus.emit("UI:FILTER", {
23748
+ const payload = {
23695
23749
  entity,
23696
23750
  field,
23697
23751
  value: value === "all" || value === null ? "" : value,
23698
23752
  query
23699
- });
23753
+ };
23754
+ if (event && event !== "FILTER") {
23755
+ eventBus.emit(`UI:${event}`, payload);
23756
+ }
23757
+ eventBus.emit("UI:FILTER", payload);
23700
23758
  },
23701
- [onFilterChange, queryState, eventBus, entity, query]
23759
+ [onFilterChange, queryState, eventBus, entity, query, event]
23702
23760
  );
23703
23761
  const handleClearAll = useCallback(() => {
23704
23762
  setSelectedValues({});
@@ -23706,8 +23764,11 @@ var init_FilterGroup = __esm({
23706
23764
  queryState.clearFilters();
23707
23765
  }
23708
23766
  onClearAll?.();
23767
+ if (clearEvent && clearEvent !== "CLEAR_FILTERS") {
23768
+ eventBus.emit(`UI:${clearEvent}`, { entity, query });
23769
+ }
23709
23770
  eventBus.emit("UI:CLEAR_FILTERS", { entity, query });
23710
- }, [onClearAll, queryState, eventBus, entity, query]);
23771
+ }, [onClearAll, queryState, eventBus, entity, query, clearEvent]);
23711
23772
  const activeFilterCount = Object.keys(selectedValues).length;
23712
23773
  if (variant === "pills") {
23713
23774
  return /* @__PURE__ */ jsxs(
@@ -24835,7 +24896,7 @@ var init_SearchInput = __esm({
24835
24896
  queryState.setSearch(newValue);
24836
24897
  }
24837
24898
  onSearch?.(newValue);
24838
- if (event) {
24899
+ if (event && event !== "SEARCH") {
24839
24900
  eventBus.emit(`UI:${event}`, { searchTerm: newValue, entity });
24840
24901
  }
24841
24902
  if (event || entity || query) {
@@ -44251,6 +44312,14 @@ function createSharedEntityWriter(binding, tick, traitStatesRef, emit) {
44251
44312
  return writes;
44252
44313
  };
44253
44314
  }
44315
+ function containsConfigForward(value) {
44316
+ if (typeof value === "string") return value.startsWith("@config.");
44317
+ if (Array.isArray(value)) return value.some(containsConfigForward);
44318
+ if (value !== null && typeof value === "object") {
44319
+ return Object.values(value).some(containsConfigForward);
44320
+ }
44321
+ return false;
44322
+ }
44254
44323
  function getBindingConfig(binding) {
44255
44324
  const raw = binding.config;
44256
44325
  const normalized = normalizeCallSiteConfigToValues(raw);
@@ -44603,7 +44672,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
44603
44672
  const sharedCallSite = sharedCallSiteRaw ? resolveCallSitePayloadCaptures(
44604
44673
  Object.fromEntries(
44605
44674
  Object.entries(sharedCallSiteRaw).filter(
44606
- ([, v]) => !(typeof v === "string" && v.startsWith("@config."))
44675
+ ([, v]) => !containsConfigForward(v)
44607
44676
  )
44608
44677
  ),
44609
44678
  payload || {}
@@ -44668,7 +44737,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
44668
44737
  const callSiteOverrides = callSiteConfig ? resolveCallSitePayloadCaptures(
44669
44738
  Object.fromEntries(
44670
44739
  Object.entries(callSiteConfig).filter(
44671
- ([, v]) => !(typeof v === "string" && v.startsWith("@config."))
44740
+ ([, v]) => !containsConfigForward(v)
44672
44741
  )
44673
44742
  ),
44674
44743
  payload || {}
@@ -44910,7 +44979,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
44910
44979
  }
44911
44980
  return () => scheduler.stopAll();
44912
44981
  }, [traitBindings, runTickEffects, sharedGroups, sharedEntityStore, emitFromSharedWriter]);
44913
- const processEventQueued = useCallback(async (eventKey, payload) => {
44982
+ const processEventQueued = useCallback(async (eventKey, payload, targetTrait) => {
44914
44983
  const normalizedEvent = normalizeEventKey(eventKey);
44915
44984
  const bindings = traitBindingsRef.current;
44916
44985
  const currentManager = managerRef.current;
@@ -44939,7 +45008,9 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
44939
45008
  normalizedEvent,
44940
45009
  payload,
44941
45010
  void 0,
44942
- entityByTrait
45011
+ entityByTrait,
45012
+ void 0,
45013
+ targetTrait
44943
45014
  );
44944
45015
  crossTraitLog.debug("processEvent:results", {
44945
45016
  event: normalizedEvent,
@@ -45032,8 +45103,11 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
45032
45103
  const effectTraces = result.effects.map(
45033
45104
  (e) => {
45034
45105
  if (Array.isArray(e)) {
45106
+ const head = String(e[0] ?? "unknown");
45107
+ const entityName = head === "fetch" && typeof e[1] === "string" ? e[1] : head === "persist" && typeof e[2] === "string" ? e[2] : void 0;
45035
45108
  return {
45036
- type: String(e[0] ?? "unknown"),
45109
+ type: head,
45110
+ ...entityName !== void 0 ? { entityName } : {},
45037
45111
  args: e.slice(1),
45038
45112
  status: "executed"
45039
45113
  };
@@ -45086,7 +45160,8 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
45086
45160
  const orbital = orbitalsByTrait?.[traitName];
45087
45161
  if (orbital) dispatchedOrbitals.add(orbital);
45088
45162
  }
45089
- await onEventProcessed(normalizedEvent, payload, dispatchedOrbitals);
45163
+ const relayPayload = targetTrait !== void 0 ? { ...payload ?? {}, _targetTrait: targetTrait } : payload;
45164
+ await onEventProcessed(normalizedEvent, relayPayload, dispatchedOrbitals);
45090
45165
  }
45091
45166
  }, [entities, eventBus, sharedEntityStore]);
45092
45167
  const drainEventQueue = useCallback(async () => {
@@ -45095,14 +45170,14 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
45095
45170
  try {
45096
45171
  while (eventQueueRef.current.length > 0) {
45097
45172
  const entry = eventQueueRef.current.shift();
45098
- await processEventQueued(entry.eventKey, entry.payload);
45173
+ await processEventQueued(entry.eventKey, entry.payload, entry.targetTrait);
45099
45174
  }
45100
45175
  } finally {
45101
45176
  processingRef.current = false;
45102
45177
  }
45103
45178
  }, [processEventQueued]);
45104
- const enqueueAndDrain = useCallback((eventKey, payload) => {
45105
- eventQueueRef.current.push({ eventKey, payload });
45179
+ const enqueueAndDrain = useCallback((eventKey, payload, targetTrait) => {
45180
+ eventQueueRef.current.push({ eventKey, payload, targetTrait });
45106
45181
  void drainEventQueue();
45107
45182
  }, [drainEventQueue]);
45108
45183
  useCallback((eventKey, payload) => {
@@ -45199,7 +45274,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
45199
45274
  crossTraitLog.debug("listen:subscribed", { busKey, targetTrait: binding.trait.name, sourceOrbital, sourceTrait, listenEvent: listen.event, triggers: listen.triggers });
45200
45275
  const unsub = eventBus.on(busKey, (event) => {
45201
45276
  crossTraitLog.debug("listen:fired", { busKey, targetTrait: binding.trait.name, triggers: listen.triggers });
45202
- enqueueAndDrain(listen.triggers, event.payload);
45277
+ enqueueAndDrain(listen.triggers, event.payload, binding.trait.name);
45203
45278
  });
45204
45279
  unsubscribes.push(() => {
45205
45280
  crossTraitLog.debug("listen:unsubscribe", { busKey, targetTrait: binding.trait.name, triggers: listen.triggers });
@@ -45767,6 +45842,15 @@ function OrbPreview({
45767
45842
  }
45768
45843
  }
45769
45844
  }, [pages]);
45845
+ useEffect(() => {
45846
+ const unsubscribe = eventBus.on("UI:NAVIGATE", (event) => {
45847
+ const url = event.payload?.url;
45848
+ if (typeof url === "string" && url.length > 0) {
45849
+ handleNavigate(url);
45850
+ }
45851
+ });
45852
+ return unsubscribe;
45853
+ }, [eventBus, handleNavigate]);
45770
45854
  if (!parseResult.ok) {
45771
45855
  return /* @__PURE__ */ jsx(Box, { className, style: { height }, children: /* @__PURE__ */ jsxs(Typography, { as: "pre", color: "error", variant: "small", className: "font-mono whitespace-pre-wrap break-all m-0 p-4", children: [
45772
45856
  "Parse error: ",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@almadar/ui",
3
- "version": "5.122.13",
3
+ "version": "5.123.0",
4
4
  "description": "React UI components, hooks, and providers for Almadar",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -117,11 +117,11 @@
117
117
  "access": "public"
118
118
  },
119
119
  "dependencies": {
120
- "@almadar/core": "^10.30.0",
121
- "@almadar/evaluator": "^2.30.0",
120
+ "@almadar/core": "^10.32.0",
121
+ "@almadar/evaluator": "^2.32.0",
122
122
  "@almadar/logger": "^1.9.0",
123
- "@almadar/runtime": "^6.33.0",
124
- "@almadar/std": "^16.140.0",
123
+ "@almadar/runtime": "^6.35.0",
124
+ "@almadar/std": "^16.142.0",
125
125
  "@almadar/syntax": "^1.11.0",
126
126
  "@dnd-kit/core": "^6.3.1",
127
127
  "@dnd-kit/sortable": "^10.0.0",