@almadar/ui 4.0.1 → 4.2.1

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
@@ -4084,12 +4084,42 @@ var init_EntitySchemaContext = __esm({
4084
4084
  function generateId() {
4085
4085
  return `slot-content-${++idCounter}-${Date.now()}`;
4086
4086
  }
4087
+ function aggregateSlot(sources) {
4088
+ if (!sources) return null;
4089
+ const entries = Object.entries(sources);
4090
+ if (entries.length === 0) return null;
4091
+ if (entries.length === 1) return entries[0][1];
4092
+ const children = entries.map(([, entry]) => ({
4093
+ type: entry.pattern,
4094
+ ...entry.props
4095
+ }));
4096
+ const stackId = `slot-content-stack-${entries.map(([k]) => k).join("-")}`;
4097
+ return {
4098
+ id: stackId,
4099
+ pattern: "stack",
4100
+ props: {
4101
+ direction: "vertical",
4102
+ gap: "lg",
4103
+ children
4104
+ },
4105
+ priority: 0,
4106
+ animation: "fade",
4107
+ sourceTrait: MULTI_SOURCE_STACK_TRAIT
4108
+ };
4109
+ }
4087
4110
  function useUISlotManager() {
4088
- const [slots, setSlots] = useState(DEFAULT_SLOTS);
4111
+ const [sources, setSources] = useState(DEFAULT_SOURCES);
4089
4112
  const subscribersRef = useRef(/* @__PURE__ */ new Set());
4090
4113
  const timersRef = useRef(/* @__PURE__ */ new Map());
4091
4114
  const traitIndexRef = useRef(/* @__PURE__ */ new Map());
4092
4115
  const traitSubscribersRef = useRef(/* @__PURE__ */ new Map());
4116
+ const slots = useMemo(() => {
4117
+ const out = { ...DEFAULT_SLOTS };
4118
+ for (const slot of ALL_SLOTS) {
4119
+ out[slot] = aggregateSlot(sources[slot]);
4120
+ }
4121
+ return out;
4122
+ }, [sources]);
4093
4123
  useEffect(() => {
4094
4124
  return () => {
4095
4125
  timersRef.current.forEach((timer) => clearTimeout(timer));
@@ -4128,103 +4158,160 @@ function useUISlotManager() {
4128
4158
  const unindexTrait = useCallback((traitName) => {
4129
4159
  traitIndexRef.current.delete(traitName);
4130
4160
  }, []);
4131
- const render = useCallback((config) => {
4132
- const id = generateId();
4133
- const content = {
4134
- id,
4135
- pattern: config.pattern,
4136
- props: config.props ?? {},
4137
- priority: config.priority ?? 0,
4138
- animation: config.animation ?? "fade",
4139
- onDismiss: config.onDismiss,
4140
- sourceTrait: config.sourceTrait
4141
- };
4142
- if (config.autoDismissMs && config.autoDismissMs > 0) {
4143
- content.autoDismissAt = Date.now() + config.autoDismissMs;
4144
- const timer = setTimeout(() => {
4145
- setSlots((prev) => {
4146
- if (prev[config.target]?.id === id) {
4147
- content.onDismiss?.();
4148
- notifySubscribers(config.target, null);
4149
- return { ...prev, [config.target]: null };
4150
- }
4161
+ const render = useCallback(
4162
+ (config) => {
4163
+ const id = generateId();
4164
+ const sourceKey = config.sourceTrait ?? DEFAULT_SOURCE_KEY;
4165
+ const content = {
4166
+ id,
4167
+ pattern: config.pattern,
4168
+ props: config.props ?? {},
4169
+ priority: config.priority ?? 0,
4170
+ animation: config.animation ?? "fade",
4171
+ onDismiss: config.onDismiss,
4172
+ sourceTrait: config.sourceTrait
4173
+ };
4174
+ if (config.autoDismissMs && config.autoDismissMs > 0) {
4175
+ content.autoDismissAt = Date.now() + config.autoDismissMs;
4176
+ const timer = setTimeout(() => {
4177
+ setSources((prev) => {
4178
+ const slotSources = prev[config.target];
4179
+ if (slotSources && slotSources[sourceKey]?.id === id) {
4180
+ content.onDismiss?.();
4181
+ const next = { ...slotSources };
4182
+ delete next[sourceKey];
4183
+ const updated = { ...prev, [config.target]: next };
4184
+ notifySubscribers(config.target, aggregateSlot(next));
4185
+ return updated;
4186
+ }
4187
+ return prev;
4188
+ });
4189
+ timersRef.current.delete(id);
4190
+ }, config.autoDismissMs);
4191
+ timersRef.current.set(id, timer);
4192
+ }
4193
+ setSources((prev) => {
4194
+ const slotSources = prev[config.target] ?? {};
4195
+ const existing = slotSources[sourceKey];
4196
+ if (existing && existing.priority > content.priority) {
4197
+ console.warn(
4198
+ `[UISlots] Slot "${config.target}" source "${sourceKey}" already has higher priority content (${existing.priority} > ${content.priority})`
4199
+ );
4151
4200
  return prev;
4152
- });
4153
- timersRef.current.delete(id);
4154
- }, config.autoDismissMs);
4155
- timersRef.current.set(id, timer);
4156
- }
4157
- setSlots((prev) => {
4158
- const existing = prev[config.target];
4159
- if (existing && existing.priority > content.priority) {
4160
- console.warn(
4161
- `[UISlots] Slot "${config.target}" already has higher priority content (${existing.priority} > ${content.priority})`
4162
- );
4163
- return prev;
4164
- }
4165
- if (content.sourceTrait) {
4166
- indexTraitRender(content.sourceTrait, content);
4167
- notifyTraitSubscribers(content.sourceTrait, content);
4168
- }
4169
- notifySubscribers(config.target, content);
4170
- return { ...prev, [config.target]: content };
4171
- });
4172
- return id;
4173
- }, [notifySubscribers, notifyTraitSubscribers, indexTraitRender]);
4174
- const clear = useCallback((slot) => {
4175
- setSlots((prev) => {
4176
- const content = prev[slot];
4177
- if (content) {
4178
- const timer = timersRef.current.get(content.id);
4179
- if (timer) {
4180
- clearTimeout(timer);
4181
- timersRef.current.delete(content.id);
4182
4201
  }
4183
- content.onDismiss?.();
4202
+ const nextSources = {
4203
+ ...slotSources,
4204
+ [sourceKey]: content
4205
+ };
4206
+ const nextAll = { ...prev, [config.target]: nextSources };
4184
4207
  if (content.sourceTrait) {
4185
- unindexTrait(content.sourceTrait);
4186
- notifyTraitSubscribers(content.sourceTrait, null);
4208
+ indexTraitRender(content.sourceTrait, content);
4209
+ notifyTraitSubscribers(content.sourceTrait, content);
4210
+ }
4211
+ notifySubscribers(config.target, aggregateSlot(nextSources));
4212
+ return nextAll;
4213
+ });
4214
+ return id;
4215
+ },
4216
+ [notifySubscribers, notifyTraitSubscribers, indexTraitRender]
4217
+ );
4218
+ const clear = useCallback(
4219
+ (slot) => {
4220
+ setSources((prev) => {
4221
+ const slotSources = prev[slot];
4222
+ if (!slotSources || Object.keys(slotSources).length === 0) {
4223
+ return prev;
4224
+ }
4225
+ for (const content of Object.values(slotSources)) {
4226
+ const timer = timersRef.current.get(content.id);
4227
+ if (timer) {
4228
+ clearTimeout(timer);
4229
+ timersRef.current.delete(content.id);
4230
+ }
4231
+ content.onDismiss?.();
4232
+ if (content.sourceTrait) {
4233
+ unindexTrait(content.sourceTrait);
4234
+ notifyTraitSubscribers(content.sourceTrait, null);
4235
+ }
4187
4236
  }
4188
4237
  notifySubscribers(slot, null);
4189
- }
4190
- return { ...prev, [slot]: null };
4191
- });
4192
- }, [notifySubscribers, notifyTraitSubscribers, unindexTrait]);
4193
- const clearById = useCallback((id) => {
4194
- setSlots((prev) => {
4195
- const entry = Object.entries(prev).find(([, content]) => content?.id === id);
4196
- if (entry) {
4197
- const [slot, content] = entry;
4198
- const timer = timersRef.current.get(id);
4238
+ return { ...prev, [slot]: {} };
4239
+ });
4240
+ },
4241
+ [notifySubscribers, notifyTraitSubscribers, unindexTrait]
4242
+ );
4243
+ const clearBySource = useCallback(
4244
+ (slot, sourceTrait) => {
4245
+ const sourceKey = sourceTrait;
4246
+ setSources((prev) => {
4247
+ const slotSources = prev[slot];
4248
+ if (!slotSources || !(sourceKey in slotSources)) return prev;
4249
+ const content = slotSources[sourceKey];
4250
+ const timer = timersRef.current.get(content.id);
4199
4251
  if (timer) {
4200
4252
  clearTimeout(timer);
4201
- timersRef.current.delete(id);
4253
+ timersRef.current.delete(content.id);
4202
4254
  }
4203
4255
  content.onDismiss?.();
4204
4256
  if (content.sourceTrait) {
4205
4257
  unindexTrait(content.sourceTrait);
4206
4258
  notifyTraitSubscribers(content.sourceTrait, null);
4207
4259
  }
4208
- notifySubscribers(slot, null);
4209
- return { ...prev, [slot]: null };
4210
- }
4211
- return prev;
4212
- });
4213
- }, [notifySubscribers, notifyTraitSubscribers, unindexTrait]);
4260
+ const nextSources = { ...slotSources };
4261
+ delete nextSources[sourceKey];
4262
+ notifySubscribers(slot, aggregateSlot(nextSources));
4263
+ return { ...prev, [slot]: nextSources };
4264
+ });
4265
+ },
4266
+ [notifySubscribers, notifyTraitSubscribers, unindexTrait]
4267
+ );
4268
+ const clearById = useCallback(
4269
+ (id) => {
4270
+ setSources((prev) => {
4271
+ for (const slot of ALL_SLOTS) {
4272
+ const slotSources = prev[slot];
4273
+ if (!slotSources) continue;
4274
+ const matchKey = Object.keys(slotSources).find(
4275
+ (k) => slotSources[k].id === id
4276
+ );
4277
+ if (!matchKey) continue;
4278
+ const content = slotSources[matchKey];
4279
+ const timer = timersRef.current.get(id);
4280
+ if (timer) {
4281
+ clearTimeout(timer);
4282
+ timersRef.current.delete(id);
4283
+ }
4284
+ content.onDismiss?.();
4285
+ if (content.sourceTrait) {
4286
+ unindexTrait(content.sourceTrait);
4287
+ notifyTraitSubscribers(content.sourceTrait, null);
4288
+ }
4289
+ const nextSources = { ...slotSources };
4290
+ delete nextSources[matchKey];
4291
+ notifySubscribers(slot, aggregateSlot(nextSources));
4292
+ return { ...prev, [slot]: nextSources };
4293
+ }
4294
+ return prev;
4295
+ });
4296
+ },
4297
+ [notifySubscribers, notifyTraitSubscribers, unindexTrait]
4298
+ );
4214
4299
  const clearAll = useCallback(() => {
4215
4300
  timersRef.current.forEach((timer) => clearTimeout(timer));
4216
4301
  timersRef.current.clear();
4217
- setSlots((prev) => {
4218
- Object.entries(prev).forEach(([slot, content]) => {
4219
- if (content) {
4302
+ setSources((prev) => {
4303
+ for (const slot of ALL_SLOTS) {
4304
+ const slotSources = prev[slot];
4305
+ if (!slotSources) continue;
4306
+ for (const content of Object.values(slotSources)) {
4220
4307
  content.onDismiss?.();
4221
4308
  if (content.sourceTrait) {
4222
4309
  notifyTraitSubscribers(content.sourceTrait, null);
4223
4310
  }
4224
- notifySubscribers(slot, null);
4225
4311
  }
4226
- });
4227
- return DEFAULT_SLOTS;
4312
+ notifySubscribers(slot, null);
4313
+ }
4314
+ return DEFAULT_SOURCES;
4228
4315
  });
4229
4316
  traitIndexRef.current.clear();
4230
4317
  }, [notifySubscribers, notifyTraitSubscribers]);
@@ -4234,16 +4321,16 @@ function useUISlotManager() {
4234
4321
  subscribersRef.current.delete(callback);
4235
4322
  };
4236
4323
  }, []);
4237
- const hasContent = useCallback((slot) => {
4238
- return slots[slot] !== null;
4239
- }, [slots]);
4240
- const getContent = useCallback((slot) => {
4241
- return slots[slot];
4242
- }, [slots]);
4324
+ const hasContent = useCallback(
4325
+ (slot) => slots[slot] !== null,
4326
+ [slots]
4327
+ );
4328
+ const getContent = useCallback(
4329
+ (slot) => slots[slot],
4330
+ [slots]
4331
+ );
4243
4332
  const getTraitContent = useCallback(
4244
- (traitName) => {
4245
- return traitIndexRef.current.get(traitName) ?? null;
4246
- },
4333
+ (traitName) => traitIndexRef.current.get(traitName) ?? null,
4247
4334
  []
4248
4335
  );
4249
4336
  const subscribeTrait = useCallback(
@@ -4269,6 +4356,7 @@ function useUISlotManager() {
4269
4356
  slots,
4270
4357
  render,
4271
4358
  clear,
4359
+ clearBySource,
4272
4360
  clearById,
4273
4361
  clearAll,
4274
4362
  subscribe: subscribe2,
@@ -4278,24 +4366,40 @@ function useUISlotManager() {
4278
4366
  subscribeTrait
4279
4367
  };
4280
4368
  }
4281
- var DEFAULT_SLOTS, idCounter;
4369
+ var DEFAULT_SOURCE_KEY, MULTI_SOURCE_STACK_TRAIT, ALL_SLOTS, DEFAULT_SLOTS, DEFAULT_SOURCES, idCounter;
4282
4370
  var init_useUISlots = __esm({
4283
4371
  "hooks/useUISlots.ts"() {
4284
4372
  "use client";
4285
- DEFAULT_SLOTS = {
4286
- main: null,
4287
- sidebar: null,
4288
- modal: null,
4289
- drawer: null,
4290
- overlay: null,
4291
- center: null,
4292
- toast: null,
4293
- "hud-top": null,
4294
- "hud-bottom": null,
4295
- "hud-left": null,
4296
- "hud-right": null,
4297
- floating: null
4298
- };
4373
+ DEFAULT_SOURCE_KEY = "__default__";
4374
+ MULTI_SOURCE_STACK_TRAIT = "__multi_source_stack__";
4375
+ ALL_SLOTS = [
4376
+ "main",
4377
+ "sidebar",
4378
+ "modal",
4379
+ "drawer",
4380
+ "overlay",
4381
+ "center",
4382
+ "toast",
4383
+ "hud-top",
4384
+ "hud-bottom",
4385
+ "hud-left",
4386
+ "hud-right",
4387
+ "floating"
4388
+ ];
4389
+ DEFAULT_SLOTS = ALL_SLOTS.reduce(
4390
+ (acc, slot) => {
4391
+ acc[slot] = null;
4392
+ return acc;
4393
+ },
4394
+ {}
4395
+ );
4396
+ DEFAULT_SOURCES = ALL_SLOTS.reduce(
4397
+ (acc, slot) => {
4398
+ acc[slot] = {};
4399
+ return acc;
4400
+ },
4401
+ {}
4402
+ );
4299
4403
  idCounter = 0;
4300
4404
  }
4301
4405
  });
@@ -21588,6 +21692,16 @@ function statusVariant2(value) {
21588
21692
  if (["new", "created", "scheduled", "queued", "info"].includes(v)) return "info";
21589
21693
  return "default";
21590
21694
  }
21695
+ function resolveBadgeVariant(field, value) {
21696
+ const fromMap = field.colorMap?.[value];
21697
+ if (fromMap) {
21698
+ const normalised = fromMap === "destructive" ? "danger" : fromMap;
21699
+ if (BADGE_VARIANTS.has(normalised)) {
21700
+ return normalised;
21701
+ }
21702
+ }
21703
+ return statusVariant2(value);
21704
+ }
21591
21705
  function formatDate2(value) {
21592
21706
  if (!value) return "";
21593
21707
  const d = new Date(String(value));
@@ -21801,7 +21915,7 @@ function DataGrid({
21801
21915
  if (val === void 0 || val === null) return null;
21802
21916
  return /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center", children: [
21803
21917
  field.icon && /* @__PURE__ */ jsx(Icon, { name: field.icon, size: "xs" }),
21804
- /* @__PURE__ */ jsx(Badge, { variant: statusVariant2(String(val)), children: String(val) })
21918
+ /* @__PURE__ */ jsx(Badge, { variant: resolveBadgeVariant(field, String(val)), children: String(val) })
21805
21919
  ] }, field.name);
21806
21920
  }) })
21807
21921
  ] }),
@@ -21894,7 +22008,7 @@ function DataGrid({
21894
22008
  )
21895
22009
  ] });
21896
22010
  }
21897
- var gapStyles6;
22011
+ var BADGE_VARIANTS, gapStyles6;
21898
22012
  var init_DataGrid = __esm({
21899
22013
  "components/molecules/DataGrid.tsx"() {
21900
22014
  "use client";
@@ -21909,6 +22023,17 @@ var init_DataGrid = __esm({
21909
22023
  init_Button();
21910
22024
  init_Icon();
21911
22025
  init_InfiniteScrollSentinel();
22026
+ BADGE_VARIANTS = /* @__PURE__ */ new Set([
22027
+ "default",
22028
+ "primary",
22029
+ "secondary",
22030
+ "success",
22031
+ "warning",
22032
+ "danger",
22033
+ "error",
22034
+ "info",
22035
+ "neutral"
22036
+ ]);
21912
22037
  gapStyles6 = {
21913
22038
  none: "gap-0",
21914
22039
  sm: "gap-2",
@@ -51564,7 +51689,7 @@ function useTraitStateMachine(traitBindings, slotsActions, options) {
51564
51689
  canHandleEvent
51565
51690
  };
51566
51691
  }
51567
- var DEFAULT_SOURCE_KEY = "__default__";
51692
+ var DEFAULT_SOURCE_KEY2 = "__default__";
51568
51693
  function slotEntriesInOrder(slot) {
51569
51694
  if (!slot) return [];
51570
51695
  const out = [];
@@ -51580,7 +51705,7 @@ var SlotsActionsContext = createContext(null);
51580
51705
  function SlotsProvider({ children }) {
51581
51706
  const [slots, setSlots] = useState({});
51582
51707
  const setSlotPatterns = useCallback((slot, patterns, source) => {
51583
- const sourceKey = source?.trait ?? DEFAULT_SOURCE_KEY;
51708
+ const sourceKey = source?.trait ?? DEFAULT_SOURCE_KEY2;
51584
51709
  setSlots((prev) => {
51585
51710
  const prevSlot = prev[slot] ?? {};
51586
51711
  return {
@@ -51906,13 +52031,11 @@ function SlotBridge() {
51906
52031
  return null;
51907
52032
  }
51908
52033
  function applyServerEffects(effects, uiSlots, onNavigate) {
51909
- const perSlotRenders = /* @__PURE__ */ new Map();
51910
52034
  for (const eff of effects) {
51911
52035
  if (eff.type === "render-ui" && eff.slot && eff.pattern) {
51912
52036
  const patternRecord = eff.pattern;
51913
52037
  const { type: patternType, children, ...inlineProps } = patternRecord;
51914
52038
  const normalizedChildren = Array.isArray(children) ? children.map((c) => normalizeChild(c)) : children;
51915
- const sourceTrait = eff.traitName ?? "server";
51916
52039
  uiSlots.render({
51917
52040
  target: eff.slot,
51918
52041
  pattern: patternType,
@@ -51920,38 +52043,12 @@ function applyServerEffects(effects, uiSlots, onNavigate) {
51920
52043
  ...inlineProps,
51921
52044
  ...normalizedChildren !== void 0 ? { children: normalizedChildren } : {}
51922
52045
  },
51923
- sourceTrait
51924
- });
51925
- const bucket = perSlotRenders.get(eff.slot) ?? [];
51926
- bucket.push({
51927
- sourceTrait,
51928
- pattern: {
51929
- type: patternType,
51930
- ...inlineProps,
51931
- ...normalizedChildren !== void 0 ? { children: normalizedChildren } : {}
51932
- }
52046
+ sourceTrait: eff.traitName ?? "server"
51933
52047
  });
51934
- perSlotRenders.set(eff.slot, bucket);
51935
52048
  } else if (eff.type === "navigate" && eff.route && onNavigate) {
51936
52049
  onNavigate(eff.route, eff.params);
51937
52050
  }
51938
52051
  }
51939
- for (const [slot, bucket] of perSlotRenders) {
51940
- const distinctSources = new Set(bucket.map((b) => b.sourceTrait));
51941
- if (distinctSources.size <= 1) continue;
51942
- uiSlots.render({
51943
- target: slot,
51944
- pattern: "stack",
51945
- props: {
51946
- direction: "vertical",
51947
- gap: "lg",
51948
- children: bucket.map((b) => b.pattern)
51949
- },
51950
- // Use a synthetic wrapper source trait; individual traits' frames
51951
- // already live in the per-trait index from the per-effect loop.
51952
- sourceTrait: "__multi_source_stack__"
51953
- });
51954
- }
51955
52052
  }
51956
52053
  function TraitInitializer({ traits: traits2, orbitalNames, onNavigate, onLocalFallback, persistence }) {
51957
52054
  const slotsActions = useSlotsActions();