@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.
@@ -4130,12 +4130,42 @@ var init_EntitySchemaContext = __esm({
4130
4130
  function generateId() {
4131
4131
  return `slot-content-${++idCounter}-${Date.now()}`;
4132
4132
  }
4133
+ function aggregateSlot(sources) {
4134
+ if (!sources) return null;
4135
+ const entries = Object.entries(sources);
4136
+ if (entries.length === 0) return null;
4137
+ if (entries.length === 1) return entries[0][1];
4138
+ const children = entries.map(([, entry]) => ({
4139
+ type: entry.pattern,
4140
+ ...entry.props
4141
+ }));
4142
+ const stackId = `slot-content-stack-${entries.map(([k]) => k).join("-")}`;
4143
+ return {
4144
+ id: stackId,
4145
+ pattern: "stack",
4146
+ props: {
4147
+ direction: "vertical",
4148
+ gap: "lg",
4149
+ children
4150
+ },
4151
+ priority: 0,
4152
+ animation: "fade",
4153
+ sourceTrait: MULTI_SOURCE_STACK_TRAIT
4154
+ };
4155
+ }
4133
4156
  function useUISlotManager() {
4134
- const [slots, setSlots] = React126.useState(DEFAULT_SLOTS);
4157
+ const [sources, setSources] = React126.useState(DEFAULT_SOURCES);
4135
4158
  const subscribersRef = React126.useRef(/* @__PURE__ */ new Set());
4136
4159
  const timersRef = React126.useRef(/* @__PURE__ */ new Map());
4137
4160
  const traitIndexRef = React126.useRef(/* @__PURE__ */ new Map());
4138
4161
  const traitSubscribersRef = React126.useRef(/* @__PURE__ */ new Map());
4162
+ const slots = React126.useMemo(() => {
4163
+ const out = { ...DEFAULT_SLOTS };
4164
+ for (const slot of ALL_SLOTS) {
4165
+ out[slot] = aggregateSlot(sources[slot]);
4166
+ }
4167
+ return out;
4168
+ }, [sources]);
4139
4169
  React126.useEffect(() => {
4140
4170
  return () => {
4141
4171
  timersRef.current.forEach((timer) => clearTimeout(timer));
@@ -4174,103 +4204,160 @@ function useUISlotManager() {
4174
4204
  const unindexTrait = React126.useCallback((traitName) => {
4175
4205
  traitIndexRef.current.delete(traitName);
4176
4206
  }, []);
4177
- const render = React126.useCallback((config) => {
4178
- const id = generateId();
4179
- const content = {
4180
- id,
4181
- pattern: config.pattern,
4182
- props: config.props ?? {},
4183
- priority: config.priority ?? 0,
4184
- animation: config.animation ?? "fade",
4185
- onDismiss: config.onDismiss,
4186
- sourceTrait: config.sourceTrait
4187
- };
4188
- if (config.autoDismissMs && config.autoDismissMs > 0) {
4189
- content.autoDismissAt = Date.now() + config.autoDismissMs;
4190
- const timer = setTimeout(() => {
4191
- setSlots((prev) => {
4192
- if (prev[config.target]?.id === id) {
4193
- content.onDismiss?.();
4194
- notifySubscribers(config.target, null);
4195
- return { ...prev, [config.target]: null };
4196
- }
4207
+ const render = React126.useCallback(
4208
+ (config) => {
4209
+ const id = generateId();
4210
+ const sourceKey = config.sourceTrait ?? DEFAULT_SOURCE_KEY;
4211
+ const content = {
4212
+ id,
4213
+ pattern: config.pattern,
4214
+ props: config.props ?? {},
4215
+ priority: config.priority ?? 0,
4216
+ animation: config.animation ?? "fade",
4217
+ onDismiss: config.onDismiss,
4218
+ sourceTrait: config.sourceTrait
4219
+ };
4220
+ if (config.autoDismissMs && config.autoDismissMs > 0) {
4221
+ content.autoDismissAt = Date.now() + config.autoDismissMs;
4222
+ const timer = setTimeout(() => {
4223
+ setSources((prev) => {
4224
+ const slotSources = prev[config.target];
4225
+ if (slotSources && slotSources[sourceKey]?.id === id) {
4226
+ content.onDismiss?.();
4227
+ const next = { ...slotSources };
4228
+ delete next[sourceKey];
4229
+ const updated = { ...prev, [config.target]: next };
4230
+ notifySubscribers(config.target, aggregateSlot(next));
4231
+ return updated;
4232
+ }
4233
+ return prev;
4234
+ });
4235
+ timersRef.current.delete(id);
4236
+ }, config.autoDismissMs);
4237
+ timersRef.current.set(id, timer);
4238
+ }
4239
+ setSources((prev) => {
4240
+ const slotSources = prev[config.target] ?? {};
4241
+ const existing = slotSources[sourceKey];
4242
+ if (existing && existing.priority > content.priority) {
4243
+ console.warn(
4244
+ `[UISlots] Slot "${config.target}" source "${sourceKey}" already has higher priority content (${existing.priority} > ${content.priority})`
4245
+ );
4197
4246
  return prev;
4198
- });
4199
- timersRef.current.delete(id);
4200
- }, config.autoDismissMs);
4201
- timersRef.current.set(id, timer);
4202
- }
4203
- setSlots((prev) => {
4204
- const existing = prev[config.target];
4205
- if (existing && existing.priority > content.priority) {
4206
- console.warn(
4207
- `[UISlots] Slot "${config.target}" already has higher priority content (${existing.priority} > ${content.priority})`
4208
- );
4209
- return prev;
4210
- }
4211
- if (content.sourceTrait) {
4212
- indexTraitRender(content.sourceTrait, content);
4213
- notifyTraitSubscribers(content.sourceTrait, content);
4214
- }
4215
- notifySubscribers(config.target, content);
4216
- return { ...prev, [config.target]: content };
4217
- });
4218
- return id;
4219
- }, [notifySubscribers, notifyTraitSubscribers, indexTraitRender]);
4220
- const clear = React126.useCallback((slot) => {
4221
- setSlots((prev) => {
4222
- const content = prev[slot];
4223
- if (content) {
4224
- const timer = timersRef.current.get(content.id);
4225
- if (timer) {
4226
- clearTimeout(timer);
4227
- timersRef.current.delete(content.id);
4228
4247
  }
4229
- content.onDismiss?.();
4248
+ const nextSources = {
4249
+ ...slotSources,
4250
+ [sourceKey]: content
4251
+ };
4252
+ const nextAll = { ...prev, [config.target]: nextSources };
4230
4253
  if (content.sourceTrait) {
4231
- unindexTrait(content.sourceTrait);
4232
- notifyTraitSubscribers(content.sourceTrait, null);
4254
+ indexTraitRender(content.sourceTrait, content);
4255
+ notifyTraitSubscribers(content.sourceTrait, content);
4256
+ }
4257
+ notifySubscribers(config.target, aggregateSlot(nextSources));
4258
+ return nextAll;
4259
+ });
4260
+ return id;
4261
+ },
4262
+ [notifySubscribers, notifyTraitSubscribers, indexTraitRender]
4263
+ );
4264
+ const clear = React126.useCallback(
4265
+ (slot) => {
4266
+ setSources((prev) => {
4267
+ const slotSources = prev[slot];
4268
+ if (!slotSources || Object.keys(slotSources).length === 0) {
4269
+ return prev;
4270
+ }
4271
+ for (const content of Object.values(slotSources)) {
4272
+ const timer = timersRef.current.get(content.id);
4273
+ if (timer) {
4274
+ clearTimeout(timer);
4275
+ timersRef.current.delete(content.id);
4276
+ }
4277
+ content.onDismiss?.();
4278
+ if (content.sourceTrait) {
4279
+ unindexTrait(content.sourceTrait);
4280
+ notifyTraitSubscribers(content.sourceTrait, null);
4281
+ }
4233
4282
  }
4234
4283
  notifySubscribers(slot, null);
4235
- }
4236
- return { ...prev, [slot]: null };
4237
- });
4238
- }, [notifySubscribers, notifyTraitSubscribers, unindexTrait]);
4239
- const clearById = React126.useCallback((id) => {
4240
- setSlots((prev) => {
4241
- const entry = Object.entries(prev).find(([, content]) => content?.id === id);
4242
- if (entry) {
4243
- const [slot, content] = entry;
4244
- const timer = timersRef.current.get(id);
4284
+ return { ...prev, [slot]: {} };
4285
+ });
4286
+ },
4287
+ [notifySubscribers, notifyTraitSubscribers, unindexTrait]
4288
+ );
4289
+ const clearBySource = React126.useCallback(
4290
+ (slot, sourceTrait) => {
4291
+ const sourceKey = sourceTrait;
4292
+ setSources((prev) => {
4293
+ const slotSources = prev[slot];
4294
+ if (!slotSources || !(sourceKey in slotSources)) return prev;
4295
+ const content = slotSources[sourceKey];
4296
+ const timer = timersRef.current.get(content.id);
4245
4297
  if (timer) {
4246
4298
  clearTimeout(timer);
4247
- timersRef.current.delete(id);
4299
+ timersRef.current.delete(content.id);
4248
4300
  }
4249
4301
  content.onDismiss?.();
4250
4302
  if (content.sourceTrait) {
4251
4303
  unindexTrait(content.sourceTrait);
4252
4304
  notifyTraitSubscribers(content.sourceTrait, null);
4253
4305
  }
4254
- notifySubscribers(slot, null);
4255
- return { ...prev, [slot]: null };
4256
- }
4257
- return prev;
4258
- });
4259
- }, [notifySubscribers, notifyTraitSubscribers, unindexTrait]);
4306
+ const nextSources = { ...slotSources };
4307
+ delete nextSources[sourceKey];
4308
+ notifySubscribers(slot, aggregateSlot(nextSources));
4309
+ return { ...prev, [slot]: nextSources };
4310
+ });
4311
+ },
4312
+ [notifySubscribers, notifyTraitSubscribers, unindexTrait]
4313
+ );
4314
+ const clearById = React126.useCallback(
4315
+ (id) => {
4316
+ setSources((prev) => {
4317
+ for (const slot of ALL_SLOTS) {
4318
+ const slotSources = prev[slot];
4319
+ if (!slotSources) continue;
4320
+ const matchKey = Object.keys(slotSources).find(
4321
+ (k) => slotSources[k].id === id
4322
+ );
4323
+ if (!matchKey) continue;
4324
+ const content = slotSources[matchKey];
4325
+ const timer = timersRef.current.get(id);
4326
+ if (timer) {
4327
+ clearTimeout(timer);
4328
+ timersRef.current.delete(id);
4329
+ }
4330
+ content.onDismiss?.();
4331
+ if (content.sourceTrait) {
4332
+ unindexTrait(content.sourceTrait);
4333
+ notifyTraitSubscribers(content.sourceTrait, null);
4334
+ }
4335
+ const nextSources = { ...slotSources };
4336
+ delete nextSources[matchKey];
4337
+ notifySubscribers(slot, aggregateSlot(nextSources));
4338
+ return { ...prev, [slot]: nextSources };
4339
+ }
4340
+ return prev;
4341
+ });
4342
+ },
4343
+ [notifySubscribers, notifyTraitSubscribers, unindexTrait]
4344
+ );
4260
4345
  const clearAll = React126.useCallback(() => {
4261
4346
  timersRef.current.forEach((timer) => clearTimeout(timer));
4262
4347
  timersRef.current.clear();
4263
- setSlots((prev) => {
4264
- Object.entries(prev).forEach(([slot, content]) => {
4265
- if (content) {
4348
+ setSources((prev) => {
4349
+ for (const slot of ALL_SLOTS) {
4350
+ const slotSources = prev[slot];
4351
+ if (!slotSources) continue;
4352
+ for (const content of Object.values(slotSources)) {
4266
4353
  content.onDismiss?.();
4267
4354
  if (content.sourceTrait) {
4268
4355
  notifyTraitSubscribers(content.sourceTrait, null);
4269
4356
  }
4270
- notifySubscribers(slot, null);
4271
4357
  }
4272
- });
4273
- return DEFAULT_SLOTS;
4358
+ notifySubscribers(slot, null);
4359
+ }
4360
+ return DEFAULT_SOURCES;
4274
4361
  });
4275
4362
  traitIndexRef.current.clear();
4276
4363
  }, [notifySubscribers, notifyTraitSubscribers]);
@@ -4280,16 +4367,16 @@ function useUISlotManager() {
4280
4367
  subscribersRef.current.delete(callback);
4281
4368
  };
4282
4369
  }, []);
4283
- const hasContent = React126.useCallback((slot) => {
4284
- return slots[slot] !== null;
4285
- }, [slots]);
4286
- const getContent = React126.useCallback((slot) => {
4287
- return slots[slot];
4288
- }, [slots]);
4370
+ const hasContent = React126.useCallback(
4371
+ (slot) => slots[slot] !== null,
4372
+ [slots]
4373
+ );
4374
+ const getContent = React126.useCallback(
4375
+ (slot) => slots[slot],
4376
+ [slots]
4377
+ );
4289
4378
  const getTraitContent = React126.useCallback(
4290
- (traitName) => {
4291
- return traitIndexRef.current.get(traitName) ?? null;
4292
- },
4379
+ (traitName) => traitIndexRef.current.get(traitName) ?? null,
4293
4380
  []
4294
4381
  );
4295
4382
  const subscribeTrait = React126.useCallback(
@@ -4315,6 +4402,7 @@ function useUISlotManager() {
4315
4402
  slots,
4316
4403
  render,
4317
4404
  clear,
4405
+ clearBySource,
4318
4406
  clearById,
4319
4407
  clearAll,
4320
4408
  subscribe: subscribe2,
@@ -4324,24 +4412,40 @@ function useUISlotManager() {
4324
4412
  subscribeTrait
4325
4413
  };
4326
4414
  }
4327
- var DEFAULT_SLOTS, idCounter;
4415
+ var DEFAULT_SOURCE_KEY, MULTI_SOURCE_STACK_TRAIT, ALL_SLOTS, DEFAULT_SLOTS, DEFAULT_SOURCES, idCounter;
4328
4416
  var init_useUISlots = __esm({
4329
4417
  "hooks/useUISlots.ts"() {
4330
4418
  "use client";
4331
- DEFAULT_SLOTS = {
4332
- main: null,
4333
- sidebar: null,
4334
- modal: null,
4335
- drawer: null,
4336
- overlay: null,
4337
- center: null,
4338
- toast: null,
4339
- "hud-top": null,
4340
- "hud-bottom": null,
4341
- "hud-left": null,
4342
- "hud-right": null,
4343
- floating: null
4344
- };
4419
+ DEFAULT_SOURCE_KEY = "__default__";
4420
+ MULTI_SOURCE_STACK_TRAIT = "__multi_source_stack__";
4421
+ ALL_SLOTS = [
4422
+ "main",
4423
+ "sidebar",
4424
+ "modal",
4425
+ "drawer",
4426
+ "overlay",
4427
+ "center",
4428
+ "toast",
4429
+ "hud-top",
4430
+ "hud-bottom",
4431
+ "hud-left",
4432
+ "hud-right",
4433
+ "floating"
4434
+ ];
4435
+ DEFAULT_SLOTS = ALL_SLOTS.reduce(
4436
+ (acc, slot) => {
4437
+ acc[slot] = null;
4438
+ return acc;
4439
+ },
4440
+ {}
4441
+ );
4442
+ DEFAULT_SOURCES = ALL_SLOTS.reduce(
4443
+ (acc, slot) => {
4444
+ acc[slot] = {};
4445
+ return acc;
4446
+ },
4447
+ {}
4448
+ );
4345
4449
  idCounter = 0;
4346
4450
  }
4347
4451
  });
@@ -21634,6 +21738,16 @@ function statusVariant2(value) {
21634
21738
  if (["new", "created", "scheduled", "queued", "info"].includes(v)) return "info";
21635
21739
  return "default";
21636
21740
  }
21741
+ function resolveBadgeVariant(field, value) {
21742
+ const fromMap = field.colorMap?.[value];
21743
+ if (fromMap) {
21744
+ const normalised = fromMap === "destructive" ? "danger" : fromMap;
21745
+ if (BADGE_VARIANTS.has(normalised)) {
21746
+ return normalised;
21747
+ }
21748
+ }
21749
+ return statusVariant2(value);
21750
+ }
21637
21751
  function formatDate2(value) {
21638
21752
  if (!value) return "";
21639
21753
  const d = new Date(String(value));
@@ -21847,7 +21961,7 @@ function DataGrid({
21847
21961
  if (val === void 0 || val === null) return null;
21848
21962
  return /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", className: "items-center", children: [
21849
21963
  field.icon && /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: field.icon, size: "xs" }),
21850
- /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: statusVariant2(String(val)), children: String(val) })
21964
+ /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: resolveBadgeVariant(field, String(val)), children: String(val) })
21851
21965
  ] }, field.name);
21852
21966
  }) })
21853
21967
  ] }),
@@ -21940,7 +22054,7 @@ function DataGrid({
21940
22054
  )
21941
22055
  ] });
21942
22056
  }
21943
- var gapStyles6;
22057
+ var BADGE_VARIANTS, gapStyles6;
21944
22058
  var init_DataGrid = __esm({
21945
22059
  "components/molecules/DataGrid.tsx"() {
21946
22060
  "use client";
@@ -21955,6 +22069,17 @@ var init_DataGrid = __esm({
21955
22069
  init_Button();
21956
22070
  init_Icon();
21957
22071
  init_InfiniteScrollSentinel();
22072
+ BADGE_VARIANTS = /* @__PURE__ */ new Set([
22073
+ "default",
22074
+ "primary",
22075
+ "secondary",
22076
+ "success",
22077
+ "warning",
22078
+ "danger",
22079
+ "error",
22080
+ "info",
22081
+ "neutral"
22082
+ ]);
21958
22083
  gapStyles6 = {
21959
22084
  none: "gap-0",
21960
22085
  sm: "gap-2",
@@ -51610,7 +51735,7 @@ function useTraitStateMachine(traitBindings, slotsActions, options) {
51610
51735
  canHandleEvent
51611
51736
  };
51612
51737
  }
51613
- var DEFAULT_SOURCE_KEY = "__default__";
51738
+ var DEFAULT_SOURCE_KEY2 = "__default__";
51614
51739
  function slotEntriesInOrder(slot) {
51615
51740
  if (!slot) return [];
51616
51741
  const out = [];
@@ -51626,7 +51751,7 @@ var SlotsActionsContext = React126.createContext(null);
51626
51751
  function SlotsProvider({ children }) {
51627
51752
  const [slots, setSlots] = React126.useState({});
51628
51753
  const setSlotPatterns = React126.useCallback((slot, patterns, source) => {
51629
- const sourceKey = source?.trait ?? DEFAULT_SOURCE_KEY;
51754
+ const sourceKey = source?.trait ?? DEFAULT_SOURCE_KEY2;
51630
51755
  setSlots((prev) => {
51631
51756
  const prevSlot = prev[slot] ?? {};
51632
51757
  return {
@@ -51952,13 +52077,11 @@ function SlotBridge() {
51952
52077
  return null;
51953
52078
  }
51954
52079
  function applyServerEffects(effects, uiSlots, onNavigate) {
51955
- const perSlotRenders = /* @__PURE__ */ new Map();
51956
52080
  for (const eff of effects) {
51957
52081
  if (eff.type === "render-ui" && eff.slot && eff.pattern) {
51958
52082
  const patternRecord = eff.pattern;
51959
52083
  const { type: patternType, children, ...inlineProps } = patternRecord;
51960
52084
  const normalizedChildren = Array.isArray(children) ? children.map((c) => normalizeChild(c)) : children;
51961
- const sourceTrait = eff.traitName ?? "server";
51962
52085
  uiSlots.render({
51963
52086
  target: eff.slot,
51964
52087
  pattern: patternType,
@@ -51966,38 +52089,12 @@ function applyServerEffects(effects, uiSlots, onNavigate) {
51966
52089
  ...inlineProps,
51967
52090
  ...normalizedChildren !== void 0 ? { children: normalizedChildren } : {}
51968
52091
  },
51969
- sourceTrait
51970
- });
51971
- const bucket = perSlotRenders.get(eff.slot) ?? [];
51972
- bucket.push({
51973
- sourceTrait,
51974
- pattern: {
51975
- type: patternType,
51976
- ...inlineProps,
51977
- ...normalizedChildren !== void 0 ? { children: normalizedChildren } : {}
51978
- }
52092
+ sourceTrait: eff.traitName ?? "server"
51979
52093
  });
51980
- perSlotRenders.set(eff.slot, bucket);
51981
52094
  } else if (eff.type === "navigate" && eff.route && onNavigate) {
51982
52095
  onNavigate(eff.route, eff.params);
51983
52096
  }
51984
52097
  }
51985
- for (const [slot, bucket] of perSlotRenders) {
51986
- const distinctSources = new Set(bucket.map((b) => b.sourceTrait));
51987
- if (distinctSources.size <= 1) continue;
51988
- uiSlots.render({
51989
- target: slot,
51990
- pattern: "stack",
51991
- props: {
51992
- direction: "vertical",
51993
- gap: "lg",
51994
- children: bucket.map((b) => b.pattern)
51995
- },
51996
- // Use a synthetic wrapper source trait; individual traits' frames
51997
- // already live in the per-trait index from the per-effect loop.
51998
- sourceTrait: "__multi_source_stack__"
51999
- });
52000
- }
52001
52098
  }
52002
52099
  function TraitInitializer({ traits: traits2, orbitalNames, onNavigate, onLocalFallback, persistence }) {
52003
52100
  const slotsActions = useSlotsActions();