@liquidcars/atlas 0.1.33 → 0.1.34

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/README.md CHANGED
@@ -69,6 +69,11 @@ const initialView = atlas.getCameraView();
69
69
  atlas.setCameraView(initialView, { animate: false });
70
70
  atlas.restoreInitialCamera({ animate: false });
71
71
  atlas.setShellLabels(true);
72
+ atlas.setContainerOpen('distribution-agreements', true);
73
+ atlas.toggleContainer('distribution-agreements');
74
+ console.log(atlas.isContainerOpen('distribution-agreements'));
75
+ console.log(atlas.getOpenContainerIds());
76
+ atlas.restoreInitialContainerState();
72
77
  atlas.openAllContainers();
73
78
  atlas.closeAllContainers();
74
79
  atlas.setTheme({
@@ -148,7 +153,7 @@ componente aplica `break-inside: avoid` durante la impresión.
148
153
  - `theme` (opcional): configuración de `background`, `lighting`, `panel`, `toolbar`, `breadcrumbs`, `hint` y `ui`. `ui` permite mostrar u ocultar `panel`, `toolbar`, `breadcrumbs` y `hint`.
149
154
  - `render.camera` (opcional): vista inicial con `position`, `target` y `zoom`. Las coordenadas pertenecen al espacio del modelo; `zoom` conserva el encuadre relativo entre tamaños de visor.
150
155
  - `render.focus` (opcional): filtrado visual por selección. `mode` acepta `all` o `connected`, `effect` acepta `dim` o `hide`, y `layout` distingue posiciones estables (`preserve`) de una proyección recompilada (`reflow`).
151
- - `entities`: objetos con `id`, `name`, `p`, `s`, `c`, `type`, `text`, `url` y opcionalmente `parent`, `geometry`, `radius`, `segments`, `frontLabel`, `anchor` y `ports`. Las URL HTTP(S) se abren en una pestaña nueva desde el panel de información.
156
+ - `entities`: objetos con `id`, `name`, `p`, `s`, `c`, `type`, `text`, `url` y opcionalmente `parent`, `geometry`, `radius`, `segments`, `frontLabel`, `anchor` y `ports`. Un contenedor puede declarar `open: true` para comenzar abierto; el valor predeterminado es cerrado y se omite al serializar. Las URL HTTP(S) se abren en una pestaña nueva desde el panel de información.
152
157
  - `p` es la posición local respecto a `parent`; una entidad sin `parent` usa coordenadas de escena.
153
158
  - `s` son dimensiones absolutas locales `[x, y, z]`.
154
159
  - `relations` acepta objetos `{from, to, label, text, url}` o la forma corta `[from, to, label]`. `label` es el título corto de la conexión, `text` su descripción y `url` su documentación externa en el panel de información.
@@ -229,11 +234,21 @@ una caja roja de fallback y emite `lc-warning` con el código
229
234
 
230
235
  La paleta puede cambiarse después de cargar el modelo mediante `atlas.setPalette({ nombre: color })` o asignando `atlas.palette = { ... }`. El cambio actualiza las piezas, aristas y relaciones sin reconstruir el modelo.
231
236
 
232
- Las acciones de navegación y exploración también están disponibles para la página anfitriona: `zoomIn()`, `zoomOut()`, `panBy()`, `panTo()`, `fit()`, `frontView()`, `getCameraView()`, `setCameraView()`, `restoreInitialCamera()`, `setShellLabels()`, `toggleShellLabels()`, `setPanelVisible()`, `setFocusFilter()`, `clearFocusFilter()`, `getFocusedIds()`, `openAllContainers()` y `closeAllContainers()`. El usuario puede desplazar la vista con el botón derecho, con `Shift` + arrastre o mediante dos dedos en una pantalla táctil. Así se puede construir una barra de controles propia sin depender de la toolbar interna.
237
+ Las acciones de navegación y exploración también están disponibles para la página anfitriona: `zoomIn()`, `zoomOut()`, `panBy()`, `panTo()`, `fit()`, `frontView()`, `getCameraView()`, `setCameraView()`, `restoreInitialCamera()`, `setShellLabels()`, `toggleShellLabels()`, `setPanelVisible()`, `setFocusFilter()`, `clearFocusFilter()`, `getFocusedIds()`, `isContainerOpen()`, `getOpenContainerIds()`, `setContainerOpen()`, `setOpenContainers()`, `toggleContainer()`, `restoreInitialContainerState()`, `openAllContainers()` y `closeAllContainers()`. El usuario puede desplazar la vista con el botón derecho, con `Shift` + arrastre o mediante dos dedos en una pantalla táctil. Así se puede construir una barra de controles propia sin depender de la toolbar interna.
238
+
239
+ Cada cambio del estado vivo emite `lc-container-state-change`. Su detalle contiene
240
+ `openContainers`, `changedContainers`, `changedContainer`, `open` y `source`, de
241
+ modo que el host puede consultar o persistir una interacción sin acceder a
242
+ detalles internos del componente.
233
243
 
234
244
  Una vista inicial portable puede declararse dentro del modelo:
235
245
 
236
246
  ```yaml
247
+ entities:
248
+ - id: distribution-agreements
249
+ type: container
250
+ open: true
251
+ children: []
237
252
  render:
238
253
  focus:
239
254
  mode: connected
@@ -12408,7 +12408,7 @@ var zl = class extends HTMLElement {
12408
12408
  return ol();
12409
12409
  }
12410
12410
  constructor() {
12411
- super(), this.attachShadow({ mode: "open" }), this.shadowRoot.innerHTML = "<style>" + hl + gl + _l + "</style>" + Sl, this.canvas = this.shadowRoot.querySelector("canvas"), this.panel = this.shadowRoot.querySelector(".panel"), this.scene = new tr(), this.camera = new Kn(30, 1, .1, 2e3), this.cameraPosition = new U(), this.cameraTarget = new U(), this.raycaster = new Wa(), this.pointer = new V(), this.nodes = /* @__PURE__ */ new Map(), this.open = /* @__PURE__ */ new Set(), this.selected = null, this.selectedRelationIndex = null, this.modelData = null, this._loading = !1, this._pendingSelection = null, this.theme = pl(), this.relationMode = this.getAttribute("relation-mode") || "selected", this.focusFilter = ll({
12411
+ super(), this.attachShadow({ mode: "open" }), this.shadowRoot.innerHTML = "<style>" + hl + gl + _l + "</style>" + Sl, this.canvas = this.shadowRoot.querySelector("canvas"), this.panel = this.shadowRoot.querySelector(".panel"), this.scene = new tr(), this.camera = new Kn(30, 1, .1, 2e3), this.cameraPosition = new U(), this.cameraTarget = new U(), this.raycaster = new Wa(), this.pointer = new V(), this.nodes = /* @__PURE__ */ new Map(), this.open = /* @__PURE__ */ new Set(), this._initialOpenContainers = /* @__PURE__ */ new Set(), this.selected = null, this.selectedRelationIndex = null, this.modelData = null, this._loading = !1, this._pendingSelection = null, this.theme = pl(), this.relationMode = this.getAttribute("relation-mode") || "selected", this.focusFilter = ll({
12412
12412
  mode: this.getAttribute("focus-mode"),
12413
12413
  effect: this.getAttribute("focus-effect"),
12414
12414
  layout: this.getAttribute("focus-layout")
@@ -12671,6 +12671,40 @@ var zl = class extends HTMLElement {
12671
12671
  composed: !0
12672
12672
  }));
12673
12673
  }
12674
+ _containerEntityIds() {
12675
+ return (this.modelData?.entities || []).filter((e) => cl(e.type)).map((e) => e.id);
12676
+ }
12677
+ _declaredOpenContainerIds() {
12678
+ return (this.modelData?.entities || []).filter((e) => cl(e.type) && e.open === !0).map((e) => e.id);
12679
+ }
12680
+ _emitContainerStateChange(e, t = {}) {
12681
+ if (!e?.changed || t.emit === !1) return;
12682
+ let n = t.changedContainer || (e.changedContainers.length === 1 ? e.changedContainers[0] : null);
12683
+ this._emit("lc-container-state-change", {
12684
+ openContainers: [...e.current],
12685
+ changedContainers: [...e.changedContainers],
12686
+ changedContainer: n,
12687
+ open: typeof t.open == "boolean" ? t.open : n ? e.current.includes(n) : null,
12688
+ source: t.source || "api"
12689
+ });
12690
+ }
12691
+ _replaceOpenContainers(e, t = {}) {
12692
+ let n = new Set(e || []), r = this._containerEntityIds(), i = [...this.open], a = r.filter((e) => n.has(e)), o = new Set(i), s = new Set(a), c = [...i.filter((e) => !s.has(e)), ...a.filter((e) => !o.has(e))], l = {
12693
+ changed: c.length > 0,
12694
+ previous: i,
12695
+ current: a,
12696
+ changedContainers: c
12697
+ };
12698
+ return l.changed && (this.open = s), (l.changed || t.forceRefresh) && t.refresh !== !1 && (this._renderPanel?.(), this._rebuildRelations?.(), this.invalidate?.()), this._emitContainerStateChange(l, t), l;
12699
+ }
12700
+ _syncInitialContainerState(e = {}) {
12701
+ let t = this._initialOpenContainers || /* @__PURE__ */ new Set(), n = new Set(this._declaredOpenContainerIds()), r = e.force ? new Set(n) : new Set(this.open);
12702
+ if (!e.force) {
12703
+ for (let e of t) n.has(e) || r.delete(e);
12704
+ for (let e of n) t.has(e) || r.add(e);
12705
+ }
12706
+ return this._initialOpenContainers = n, this._replaceOpenContainers(r, e);
12707
+ }
12674
12708
  _publicEntity(e) {
12675
12709
  let t = this.modelData?.entities?.find((t) => t.id === e);
12676
12710
  return t ? {
@@ -12699,10 +12733,16 @@ var zl = class extends HTMLElement {
12699
12733
  }
12700
12734
  _commitModelChange(e, t = {}, n = {}) {
12701
12735
  let r = n.preserveSelection === !1 ? null : this.selected?.id, i = n.preserveSelection === !1 ? null : this._publicRelation(this.selectedRelationIndex), a = i?.id ?? i?.index ?? null;
12702
- return this.modelData = Tl(this.modelData), this._build({
12736
+ this.modelData = Tl(this.modelData);
12737
+ let o = this._syncInitialContainerState({
12738
+ refresh: !1,
12739
+ emit: !1,
12740
+ source: "model"
12741
+ });
12742
+ return this._build({
12703
12743
  preserveSelection: r,
12704
12744
  preserveRelation: a
12705
- }), this._emit("lc-model-change", {
12745
+ }), this._emitContainerStateChange(o, { source: "model" }), this._emit("lc-model-change", {
12706
12746
  kind: e,
12707
12747
  model: this.modelData,
12708
12748
  ...t
@@ -12760,9 +12800,7 @@ var zl = class extends HTMLElement {
12760
12800
  let i = this.modelData.relations[this.selectedRelationIndex];
12761
12801
  this.modelData.entities = this.modelData.entities.filter((e) => !r.has(e.id)), this.modelData.relations = this.modelData.relations.filter((e) => !r.has(e.from) && !r.has(e.to));
12762
12802
  let a = i ? this.modelData.relations.indexOf(i) : -1;
12763
- this.selectedRelationIndex = a >= 0 ? a : null;
12764
- for (let e of r) this.open.delete(e);
12765
- return this.selected && r.has(this.selected.id) && (this.selected = null), this._commitModelChange("entity-remove", {
12803
+ return this.selectedRelationIndex = a >= 0 ? a : null, this.selected && r.has(this.selected.id) && (this.selected = null), this._commitModelChange("entity-remove", {
12766
12804
  id: e,
12767
12805
  removed: [...r]
12768
12806
  });
@@ -12872,12 +12910,55 @@ var zl = class extends HTMLElement {
12872
12910
  toggleShellLabels() {
12873
12911
  return this.setShellLabels(!this.showShellLabels);
12874
12912
  }
12875
- openAllContainers() {
12876
- for (let e of this.nodes.values()) cl(e.type) && this.open.add(e.id);
12877
- return this._renderPanel(), this._rebuildRelations(), this.invalidate(), this;
12913
+ isContainerOpen(e) {
12914
+ return this._containerEntityIds().includes(e) && this.open.has(e);
12915
+ }
12916
+ getOpenContainerIds() {
12917
+ return this._containerEntityIds().filter((e) => this.open.has(e));
12918
+ }
12919
+ setOpenContainers(e, t = {}) {
12920
+ if (e == null || typeof e == "string" || typeof e[Symbol.iterator] != "function") throw TypeError("Los contenedores abiertos deben indicarse mediante un iterable de ids.");
12921
+ return this._replaceOpenContainers(e, t), this;
12922
+ }
12923
+ setContainerOpen(e, t = !0, n = {}) {
12924
+ let r = this.modelData?.entities?.find((t) => t.id === e);
12925
+ if (!r) throw Error(`No existe una entidad con id "${e}".`);
12926
+ if (!cl(r.type)) throw TypeError(`La entidad "${e}" no es un contenedor.`);
12927
+ let i = new Set(this.open);
12928
+ if (t) i.add(e);
12929
+ else if (i.delete(e), n.closeDescendants !== !1) for (let t of this.modelData.entities) {
12930
+ let n = t.parent, r = /* @__PURE__ */ new Set();
12931
+ for (; n && !r.has(n);) {
12932
+ if (r.add(n), n === e) {
12933
+ cl(t.type) && i.delete(t.id);
12934
+ break;
12935
+ }
12936
+ n = this.modelData.entities.find((e) => e.id === n)?.parent || null;
12937
+ }
12938
+ }
12939
+ return this._replaceOpenContainers(i, {
12940
+ ...n,
12941
+ changedContainer: e,
12942
+ open: !!t
12943
+ }), this;
12944
+ }
12945
+ toggleContainer(e, t = {}) {
12946
+ return this.setContainerOpen(e, !this.isContainerOpen(e), t);
12947
+ }
12948
+ restoreInitialContainerState(e = {}) {
12949
+ return this._replaceOpenContainers(this._initialOpenContainers, {
12950
+ ...e,
12951
+ source: e.source || "restore"
12952
+ }), this;
12953
+ }
12954
+ openAllContainers(e = {}) {
12955
+ return this.setOpenContainers(this._containerEntityIds(), e);
12878
12956
  }
12879
- closeAllContainers() {
12880
- return this.open.clear(), this.selected = null, this._renderPanel(), this._rebuildRelations(), this.invalidate(), this;
12957
+ closeAllContainers(e = {}) {
12958
+ return this.selected = null, this.setOpenContainers([], {
12959
+ ...e,
12960
+ forceRefresh: !0
12961
+ });
12881
12962
  }
12882
12963
  get styles() {
12883
12964
  return this.theme;
@@ -12923,10 +13004,14 @@ var zl = class extends HTMLElement {
12923
13004
  this._loading = !0, this._pendingSelection = null;
12924
13005
  try {
12925
13006
  let t = typeof e == "string" ? await fetch(e).then((e) => e.json()) : e;
12926
- this.modelData = Tl(t), this.selected = null, this.selectedRelationIndex = null, this.open.clear(), this._pendingPalette &&= (this.modelData.palette = {
13007
+ this.modelData = Tl(t), this.selected = null, this.selectedRelationIndex = null, this.open.clear(), this._initialOpenContainers.clear(), this._pendingPalette &&= (this.modelData.palette = {
12927
13008
  ...this.modelData.palette,
12928
13009
  ...this._pendingPalette
12929
- }, null), this.theme = this.modelData.theme, this._applyTheme(), this._build(), this._applyModelRenderOptions(), this.modelData.render?.camera && this.setCameraView(this.modelData.render.camera, { animate: !1 }), this.dispatchEvent(new CustomEvent("lc-ready", { detail: { model: this.modelData } })), this.dispatchEvent(new CustomEvent("lc-model-change", { detail: { model: this.modelData } }));
13010
+ }, null), this.theme = this.modelData.theme, this._applyTheme(), this._syncInitialContainerState({
13011
+ force: !0,
13012
+ refresh: !1,
13013
+ emit: !1
13014
+ }), this._build(), this._applyModelRenderOptions(), this.modelData.render?.camera && this.setCameraView(this.modelData.render.camera, { animate: !1 }), this.dispatchEvent(new CustomEvent("lc-ready", { detail: { model: this.modelData } })), this.dispatchEvent(new CustomEvent("lc-model-change", { detail: { model: this.modelData } }));
12930
13015
  let n = this._pendingSelection;
12931
13016
  return this._pendingSelection = null, this._loading = !1, n?.kind === "relation" ? this.selectRelation(n.index, n.options) : n?.kind === "entity" && this.select(n.id, n.options), this;
12932
13017
  } catch (e) {
@@ -13178,28 +13263,18 @@ var zl = class extends HTMLElement {
13178
13263
  return "<button class=\"relation\" data-action=\"relation\" data-relation-index=\"" + n + "\"><strong>" + Cl(e.label || (e.from === t.id ? e.to : e.from)) + "</strong><small>" + Cl(e.from) + " → " + Cl(e.to) + "</small>" + r + "</button>";
13179
13264
  }).join("");
13180
13265
  this.panel.innerHTML = "<button class=\"back\" data-action=\"overview\">← Vista de conjunto</button>" + s + "<div class=\"eyebrow\">" + Cl(cl(t.type) ? "CONTENEDOR" : t.type === "actor" ? "ACTOR EXTERNO" : "COMPONENTE") + "</div><h2>" + Cl(t.name) + "</h2><div class=\"sub\">" + Cl(t.sub || "") + "</div><p>" + Cl(t.text || "") + "</p>" + wl(t.url) + (cl(t.type) ? "<button data-action=\"toggle-shell\">" + (this.open.has(t.id) ? "Cerrar bloque" : "Abrir bloque") + "</button>" : "") + (r.length ? "<h3>CONTIENE</h3><p>" + r.length + " pieza(s) interna(s).</p>" : "") + "<h3>RELACIONES</h3>" + (c || "<p>No hay relaciones directas.</p>"), this.panel.querySelectorAll("[data-action=\"overview\"]").forEach((e) => e.addEventListener("click", () => this.reset())), this.panel.querySelectorAll("[data-action=\"path\"]").forEach((e) => e.addEventListener("click", () => this.select(e.dataset.id))), this.panel.querySelectorAll("[data-action=\"relation\"]").forEach((e) => e.addEventListener("click", () => this.selectRelation(Number(e.dataset.relationIndex), { source: "panel" }))), this.panel.querySelector("[data-action=\"toggle-shell\"]")?.addEventListener("click", () => {
13181
- if (this.open.has(t.id)) {
13182
- this.open.delete(t.id);
13183
- for (let e of this.nodes.values()) {
13184
- let n = e.parent ? this.nodes.get(e.parent) : null;
13185
- for (; n;) {
13186
- if (n.id === t.id) {
13187
- this.open.delete(e.id);
13188
- break;
13189
- }
13190
- n = n.parent ? this.nodes.get(n.parent) : null;
13191
- }
13192
- }
13193
- } else this.open.add(t.id);
13194
- this._renderPanel(), this._rebuildRelations(), this.invalidate();
13266
+ this.toggleContainer(t.id, { source: "panel" });
13195
13267
  }), this._applyTheme();
13196
13268
  }
13197
13269
  select(e, t = {}) {
13198
13270
  let n = this.nodes.get(e);
13199
13271
  if (!n) return this;
13200
- let r = n.parent ? this.nodes.get(n.parent) : null;
13201
- for (; r;) this.open.add(r.id), r = r.parent ? this.nodes.get(r.parent) : null;
13202
- return this.selected = n, this.selectedRelationIndex = null, cl(n.type) && this.open.add(n.id), this.targetFocus.copy(this._worldPosition(n)), this.targetZoom = n.parent ? 1.35 : 1.1, this._renderPanel(), this._rebuildRelations(), this.invalidate(), t.focusEvent !== !1 && this._emitFocusChange(t.source || "api"), t.emit !== !1 && this._emit("lc-select", {
13272
+ let r = new Set(this.open), i = n.parent ? this.nodes.get(n.parent) : null;
13273
+ for (; i;) r.add(i.id), i = i.parent ? this.nodes.get(i.parent) : null;
13274
+ return this.selected = n, this.selectedRelationIndex = null, cl(n.type) && r.add(n.id), this._replaceOpenContainers(r, {
13275
+ refresh: !1,
13276
+ source: t.source || "api"
13277
+ }), this.targetFocus.copy(this._worldPosition(n)), this.targetZoom = n.parent ? 1.35 : 1.1, this._renderPanel(), this._rebuildRelations(), this.invalidate(), t.focusEvent !== !1 && this._emitFocusChange(t.source || "api"), t.emit !== !1 && this._emit("lc-select", {
13203
13278
  kind: "entity",
13204
13279
  id: e,
13205
13280
  index: null,
@@ -13212,12 +13287,17 @@ var zl = class extends HTMLElement {
13212
13287
  let n = this._relationIndex(e), r = this._publicRelation(n);
13213
13288
  if (!r) return this;
13214
13289
  this.selected = null, this.selectedRelationIndex = n;
13290
+ let i = new Set(this.open);
13215
13291
  for (let e of [r.from, r.to]) {
13216
13292
  let t = this.nodes.get(e)?.parent ? this.nodes.get(this.nodes.get(e).parent) : null;
13217
- for (; t;) this.open.add(t.id), t = t.parent ? this.nodes.get(t.parent) : null;
13293
+ for (; t;) i.add(t.id), t = t.parent ? this.nodes.get(t.parent) : null;
13218
13294
  }
13219
- let i = this.nodes.get(r.from), a = this.nodes.get(r.to);
13220
- return i && a && this.targetFocus.copy(this._worldPosition(i).add(this._worldPosition(a)).multiplyScalar(.5)), this.targetZoom = 1.22, this._renderPanel(), this._rebuildRelations(), this.invalidate(), t.focusEvent !== !1 && this._emitFocusChange(t.source || "api"), t.emit !== !1 && this._emit("lc-select", {
13295
+ this._replaceOpenContainers(i, {
13296
+ refresh: !1,
13297
+ source: t.source || "api"
13298
+ });
13299
+ let a = this.nodes.get(r.from), o = this.nodes.get(r.to);
13300
+ return a && o && this.targetFocus.copy(this._worldPosition(a).add(this._worldPosition(o)).multiplyScalar(.5)), this.targetZoom = 1.22, this._renderPanel(), this._rebuildRelations(), this.invalidate(), t.focusEvent !== !1 && this._emitFocusChange(t.source || "api"), t.emit !== !1 && this._emit("lc-select", {
13221
13301
  kind: "relation",
13222
13302
  id: r.id || null,
13223
13303
  index: n,
@@ -13234,7 +13314,10 @@ var zl = class extends HTMLElement {
13234
13314
  ].includes(e) ? e : "selected", this.setAttribute("relation-mode", this.relationMode), this._syncRelationToolbar(), this._rebuildRelations(), this.invalidate(), this.dispatchEvent(new CustomEvent("lc-relation-mode", { detail: { mode: this.relationMode } }));
13235
13315
  }
13236
13316
  reset() {
13237
- this.selected = null, this.selectedRelationIndex = null, this.open.clear(), this.relationMode = "selected", this.setAttribute("relation-mode", "selected"), this._syncRelationToolbar(), this.restoreInitialCamera({ animate: !1 }), this._renderPanel(), this._rebuildRelations(), this.invalidate(), this._emitFocusChange("reset");
13317
+ this.selected = null, this.selectedRelationIndex = null, this.restoreInitialContainerState({
13318
+ refresh: !1,
13319
+ source: "reset"
13320
+ }), this.relationMode = "selected", this.setAttribute("relation-mode", "selected"), this._syncRelationToolbar(), this.restoreInitialCamera({ animate: !1 }), this._renderPanel(), this._rebuildRelations(), this.invalidate(), this._emitFocusChange("reset");
13238
13321
  }
13239
13322
  resize() {
13240
13323
  if (!this.renderer) return;
@@ -13314,7 +13397,7 @@ var zl = class extends HTMLElement {
13314
13397
  e.key === "Escape" && (this._closeToolbarMenus(), this.canvas.focus({ preventScroll: !0 }));
13315
13398
  }), this.shadowRoot.addEventListener("click", (e) => {
13316
13399
  let t = e.target.closest?.("[data-action]"), n = t?.dataset.action;
13317
- e.target.closest?.(".toolbar-menu") || this._closeToolbarMenus(), n === "minus" && this.zoomOut(), n === "plus" && this.zoomIn(), n === "fit" && this.fit(), n === "front" && this.frontView(), n === "labels" && this.toggleShellLabels(), n === "panel" && this.setPanelVisible(!this.panelVisible), n === "open-all" && this.openAllContainers(), n === "close-all" && this.closeAllContainers(), n === "relations-all" && this.setRelationMode("all"), n === "relations-selected" && this.setRelationMode("selected"), n === "relations-none" && this.setRelationMode("none"), n === "focus-all" && this.setFocusFilter({ mode: "all" }, { source: "toolbar" }), n === "focus-dim" && this.setFocusFilter({
13400
+ e.target.closest?.(".toolbar-menu") || this._closeToolbarMenus(), n === "minus" && this.zoomOut(), n === "plus" && this.zoomIn(), n === "fit" && this.fit(), n === "front" && this.frontView(), n === "labels" && this.toggleShellLabels(), n === "panel" && this.setPanelVisible(!this.panelVisible), n === "open-all" && this.openAllContainers({ source: "toolbar" }), n === "close-all" && this.closeAllContainers({ source: "toolbar" }), n === "relations-all" && this.setRelationMode("all"), n === "relations-selected" && this.setRelationMode("selected"), n === "relations-none" && this.setRelationMode("none"), n === "focus-all" && this.setFocusFilter({ mode: "all" }, { source: "toolbar" }), n === "focus-dim" && this.setFocusFilter({
13318
13401
  mode: "connected",
13319
13402
  effect: "dim"
13320
13403
  }, { source: "toolbar" }), n === "focus-hide" && this.setFocusFilter({