@flowgram.ai/panel-manager-plugin 0.1.0-alpha.23 → 0.1.0-alpha.25

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/esm/index.js CHANGED
@@ -12,6 +12,29 @@ var __decorateClass = (decorators, target, key, kind) => {
12
12
  // src/create-panel-manager-plugin.ts
13
13
  import { definePluginCreator } from "@flowgram.ai/core";
14
14
 
15
+ // src/services/panel-factory.ts
16
+ import { createStore } from "zustand/vanilla";
17
+ import { nanoid } from "nanoid";
18
+ import { inject, injectable as injectable2 } from "inversify";
19
+
20
+ // src/services/panel-restore.ts
21
+ import { injectable } from "inversify";
22
+ var PanelRestore = Symbol("PanelRestore");
23
+ var PanelRestoreImpl = class {
24
+ constructor() {
25
+ this.map = /* @__PURE__ */ new Map();
26
+ }
27
+ store(k, v) {
28
+ this.map.set(k, v);
29
+ }
30
+ restore(k) {
31
+ return this.map.get(k);
32
+ }
33
+ };
34
+ PanelRestoreImpl = __decorateClass([
35
+ injectable()
36
+ ], PanelRestoreImpl);
37
+
15
38
  // src/components/resize-bar/index.tsx
16
39
  import { useRef, useState } from "react";
17
40
  import { jsx } from "react/jsx-runtime";
@@ -111,131 +134,214 @@ var defineConfig = (config) => {
111
134
  };
112
135
  };
113
136
 
114
- // src/services/panel-manager.ts
115
- import { injectable, inject } from "inversify";
116
- import { Playground } from "@flowgram.ai/core";
137
+ // src/utils.ts
138
+ var merge = (...objs) => {
139
+ const result = {};
140
+ for (const obj of objs) {
141
+ if (!obj || typeof obj !== "object") continue;
142
+ for (const key of Object.keys(obj)) {
143
+ const value = obj[key];
144
+ if (result[key] === void 0) {
145
+ result[key] = value;
146
+ }
147
+ }
148
+ }
149
+ return result;
150
+ };
117
151
 
118
- // src/services/float-panel.ts
119
- import { Emitter } from "@flowgram.ai/utils";
152
+ // src/services/panel-factory.ts
153
+ var PanelEntityFactory = Symbol("PanelEntityFactory");
154
+ var PanelEntityFactoryConstant = Symbol("PanelEntityFactoryConstant");
155
+ var PanelEntityConfigConstant = Symbol("PanelEntityConfigConstant");
120
156
  var PANEL_SIZE_DEFAULT = 400;
121
- var FloatPanel = class {
122
- constructor(config) {
123
- this.config = config;
124
- this.elements = [];
125
- this.onUpdateEmitter = new Emitter();
126
- this.sizeMap = /* @__PURE__ */ new Map();
127
- this.onUpdate = this.onUpdateEmitter.event;
128
- this.currentFactoryKey = "";
129
- }
130
- updateSize(newSize) {
131
- this.sizeMap.set(this.currentFactoryKey, newSize);
132
- this.onUpdateEmitter.fire();
133
- }
134
- get currentSize() {
135
- return this.sizeMap.get(this.currentFactoryKey) || PANEL_SIZE_DEFAULT;
136
- }
137
- open(factory, options) {
138
- const el = factory.render(options?.props);
139
- const idx = this.elements.findIndex((e) => e.key === factory.key);
140
- this.currentFactoryKey = factory.key;
141
- if (!this.sizeMap.has(factory.key)) {
142
- this.sizeMap.set(factory.key, factory.defaultSize || PANEL_SIZE_DEFAULT);
157
+ var PanelEntity = class {
158
+ constructor() {
159
+ this.initialized = false;
160
+ /** 实例唯一标识 */
161
+ this.id = nanoid();
162
+ /** 渲染缓存 */
163
+ this.node = null;
164
+ }
165
+ get area() {
166
+ return this.config.area;
167
+ }
168
+ get mode() {
169
+ return this.config.area.startsWith("docked") ? "docked" : "floating";
170
+ }
171
+ get key() {
172
+ return this.factory.key;
173
+ }
174
+ get renderer() {
175
+ if (!this.node) {
176
+ this.node = this.factory.render(this.config.props);
143
177
  }
144
- if (idx >= 0) {
145
- this.elements[idx] = { el, key: factory.key, style: factory.style };
146
- } else {
147
- this.elements.push({ el, key: factory.key, style: factory.style });
148
- if (this.elements.length > this.config.max) {
149
- this.elements.shift();
150
- }
178
+ return this.node;
179
+ }
180
+ get fullscreen() {
181
+ return this.store.getState().fullscreen;
182
+ }
183
+ set fullscreen(next) {
184
+ this.store.setState({ fullscreen: next });
185
+ }
186
+ get resizable() {
187
+ if (this.fullscreen) {
188
+ return false;
151
189
  }
152
- this.onUpdateEmitter.fire();
190
+ return this.factory.resize !== void 0 ? this.factory.resize : this.globalConfig.autoResize;
153
191
  }
154
- get visible() {
155
- return this.elements.length > 0;
192
+ get layer() {
193
+ return document.querySelector(
194
+ this.mode ? ".gedit-flow-panel-layer-wrap-docked" : ".gedit-flow-panel-layer-wrap-floating"
195
+ );
156
196
  }
157
- close(key) {
158
- if (!key) {
159
- this.elements = [];
160
- } else {
161
- this.elements = this.elements.filter((e) => e.key !== key);
197
+ init() {
198
+ if (this.initialized) {
199
+ return;
162
200
  }
163
- this.onUpdateEmitter.fire();
201
+ this.initialized = true;
202
+ const cache = this.restore.restore(this.key);
203
+ const initialState = merge(
204
+ {
205
+ size: this.config.defaultSize,
206
+ fullscreen: this.config.fullscreen
207
+ },
208
+ cache ? cache : {},
209
+ {
210
+ size: this.factory.defaultSize || PANEL_SIZE_DEFAULT,
211
+ fullscreen: this.factory.fullscreen || false
212
+ }
213
+ );
214
+ this.store = createStore(() => initialState);
215
+ }
216
+ mergeState() {
164
217
  }
165
218
  dispose() {
166
- this.elements = [];
167
- this.onUpdateEmitter.dispose();
219
+ this.restore.store(this.key, this.store.getState());
168
220
  }
169
221
  };
222
+ __decorateClass([
223
+ inject(PanelRestore)
224
+ ], PanelEntity.prototype, "restore", 2);
225
+ __decorateClass([
226
+ inject(PanelEntityFactoryConstant)
227
+ ], PanelEntity.prototype, "factory", 2);
228
+ __decorateClass([
229
+ inject(PanelEntityConfigConstant)
230
+ ], PanelEntity.prototype, "config", 2);
231
+ __decorateClass([
232
+ inject(PanelManagerConfig)
233
+ ], PanelEntity.prototype, "globalConfig", 2);
234
+ PanelEntity = __decorateClass([
235
+ injectable2()
236
+ ], PanelEntity);
170
237
 
171
238
  // src/services/panel-manager.ts
239
+ import { injectable as injectable3, inject as inject2 } from "inversify";
240
+ import { Emitter } from "@flowgram.ai/utils";
172
241
  var PanelManager = class {
173
242
  constructor() {
174
243
  this.panelRegistry = /* @__PURE__ */ new Map();
244
+ this.panels = /* @__PURE__ */ new Map();
245
+ this.onPanelsChangeEvent = new Emitter();
246
+ this.onPanelsChange = this.onPanelsChangeEvent.event;
175
247
  }
176
248
  init() {
177
249
  this.config.factories.forEach((factory) => this.register(factory));
178
- this.right = new FloatPanel(this.config.right);
179
- this.bottom = new FloatPanel(this.config.bottom);
180
- this.dockedRight = new FloatPanel(this.config.dockedRight);
181
- this.dockedBottom = new FloatPanel(this.config.dockedBottom);
182
250
  }
251
+ /** registry panel factory */
183
252
  register(factory) {
184
253
  this.panelRegistry.set(factory.key, factory);
185
254
  }
255
+ /** open panel */
186
256
  open(key, area = "right", options) {
187
257
  const factory = this.panelRegistry.get(key);
188
258
  if (!factory) {
189
259
  return;
190
260
  }
191
- const panel = this.getPanel(area);
192
- panel.open(factory, options);
261
+ const sameKeyPanels = this.getPanels(area).filter((p) => p.key === key);
262
+ if (!factory.allowDuplicates && sameKeyPanels.length) {
263
+ sameKeyPanels.forEach((p) => this.remove(p.id));
264
+ }
265
+ const panel = this.createPanel({
266
+ factory,
267
+ config: {
268
+ area,
269
+ ...options
270
+ }
271
+ });
272
+ this.panels.set(panel.id, panel);
273
+ this.trim(area);
274
+ this.onPanelsChangeEvent.fire();
193
275
  }
276
+ /** close panel */
194
277
  close(key) {
195
- this.right.close(key);
196
- this.bottom.close(key);
197
- this.dockedRight.close(key);
198
- this.dockedBottom.close(key);
278
+ const panels = this.getPanels();
279
+ const closedPanels = key ? panels.filter((p) => p.key === key) : panels;
280
+ closedPanels.forEach((p) => this.remove(p.id));
281
+ this.onPanelsChangeEvent.fire();
282
+ }
283
+ trim(area) {
284
+ const panels = this.getPanels(area);
285
+ const areaConfig = this.getAreaConfig(area);
286
+ while (panels.length > areaConfig.max) {
287
+ const removed = panels.shift();
288
+ if (removed) {
289
+ this.remove(removed.id);
290
+ }
291
+ }
292
+ }
293
+ remove(id) {
294
+ const panel = this.panels.get(id);
295
+ if (panel) {
296
+ panel.dispose();
297
+ this.panels.delete(id);
298
+ }
199
299
  }
200
- getPanel(area) {
300
+ getPanels(area) {
301
+ const panels = [];
302
+ this.panels.forEach((panel) => {
303
+ if (!area || panel.area === area) {
304
+ panels.push(panel);
305
+ }
306
+ });
307
+ return panels;
308
+ }
309
+ getAreaConfig(area) {
201
310
  switch (area) {
202
311
  case "docked-bottom":
203
- return this.dockedBottom;
312
+ return this.config.dockedBottom;
204
313
  case "docked-right":
205
- return this.dockedRight;
314
+ return this.config.dockedRight;
206
315
  case "bottom":
207
- return this.bottom;
316
+ return this.config.bottom;
208
317
  case "right":
209
318
  default:
210
- return this.right;
319
+ return this.config.right;
211
320
  }
212
321
  }
213
322
  dispose() {
214
- this.right.dispose();
215
- this.bottom.dispose();
216
- this.dockedBottom.dispose();
217
- this.dockedRight.dispose();
323
+ this.onPanelsChangeEvent.dispose();
218
324
  }
219
325
  };
220
326
  __decorateClass([
221
- inject(Playground)
222
- ], PanelManager.prototype, "playground", 2);
223
- __decorateClass([
224
- inject(PanelManagerConfig)
327
+ inject2(PanelManagerConfig)
225
328
  ], PanelManager.prototype, "config", 2);
329
+ __decorateClass([
330
+ inject2(PanelEntityFactory)
331
+ ], PanelManager.prototype, "createPanel", 2);
226
332
  PanelManager = __decorateClass([
227
- injectable()
333
+ injectable3()
228
334
  ], PanelManager);
229
335
 
230
336
  // src/services/panel-layer.ts
231
337
  import ReactDOM from "react-dom";
232
338
  import { createElement } from "react";
233
- import { injectable as injectable2, inject as inject2 } from "inversify";
339
+ import { injectable as injectable4, inject as inject3 } from "inversify";
234
340
  import { domUtils, Disposable } from "@flowgram.ai/utils";
235
341
  import { Layer, PluginContext } from "@flowgram.ai/core";
236
342
 
237
343
  // src/components/panel-layer/panel-layer.tsx
238
- import clsx from "clsx";
344
+ import clsx2 from "clsx";
239
345
 
240
346
  // src/hooks/use-global-css.ts
241
347
  import { useEffect } from "react";
@@ -254,13 +360,106 @@ var useGlobalCSS = ({ cssText, id, cleanup }) => {
254
360
  }, [id]);
255
361
  };
256
362
 
257
- // src/components/panel-layer/float-panel.tsx
258
- import { useEffect as useEffect2, useRef as useRef2, startTransition, useState as useState2, useCallback } from "react";
363
+ // src/components/panel-layer/panel.tsx
364
+ import { useEffect as useEffect2, startTransition, useState as useState2, useRef as useRef2 } from "react";
365
+ import { clsx } from "clsx";
259
366
 
260
367
  // src/hooks/use-panel-manager.ts
261
368
  import { useService } from "@flowgram.ai/core";
262
369
  var usePanelManager = () => useService(PanelManager);
263
370
 
371
+ // src/hooks/use-panel.ts
372
+ import { useContext } from "react";
373
+ import { useStoreWithEqualityFn } from "zustand/traditional";
374
+ import { shallow } from "zustand/shallow";
375
+
376
+ // src/contexts.ts
377
+ import { createContext } from "react";
378
+ var PanelContext = createContext({});
379
+
380
+ // src/hooks/use-panel.ts
381
+ var usePanel = () => useContext(PanelContext);
382
+ var usePanelStore = (selector) => {
383
+ const panel = usePanel();
384
+ return useStoreWithEqualityFn(panel.store, selector, shallow);
385
+ };
386
+
387
+ // src/components/panel-layer/panel.tsx
388
+ import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
389
+ var PanelItem = ({ panel }) => {
390
+ const panelManager = usePanelManager();
391
+ const ref = useRef2(null);
392
+ const isHorizontal = ["right", "docked-right"].includes(panel.area);
393
+ const { size, fullscreen } = usePanelStore((s) => ({ size: s.size, fullscreen: s.fullscreen }));
394
+ const [layerSize, setLayerSize] = useState2(size);
395
+ const currentSize = fullscreen ? layerSize : size;
396
+ const sizeStyle = isHorizontal ? { width: currentSize } : { height: currentSize };
397
+ const handleResize = (next) => {
398
+ let nextSize = next;
399
+ if (typeof panel.factory.maxSize === "number" && nextSize > panel.factory.maxSize) {
400
+ nextSize = panel.factory.maxSize;
401
+ } else if (typeof panel.factory.minSize === "number" && nextSize < panel.factory.minSize) {
402
+ nextSize = panel.factory.minSize;
403
+ }
404
+ panel.store.setState({ size: nextSize });
405
+ };
406
+ useEffect2(() => {
407
+ if (ref.current && !fullscreen) {
408
+ const { width, height } = ref.current.getBoundingClientRect();
409
+ const realSize = isHorizontal ? width : height;
410
+ panel.store.setState({ size: realSize });
411
+ }
412
+ }, [fullscreen]);
413
+ useEffect2(() => {
414
+ if (!fullscreen) {
415
+ return;
416
+ }
417
+ const layer = panel.layer;
418
+ if (!layer) {
419
+ return;
420
+ }
421
+ const observer = new ResizeObserver(([entry]) => {
422
+ const { width, height } = entry.contentRect;
423
+ setLayerSize(isHorizontal ? width : height);
424
+ });
425
+ observer.observe(layer);
426
+ return () => observer.disconnect();
427
+ }, [fullscreen]);
428
+ return /* @__PURE__ */ jsxs(
429
+ "div",
430
+ {
431
+ className: clsx(
432
+ "gedit-flow-panel-wrap",
433
+ isHorizontal ? "panel-horizontal" : "panel-vertical"
434
+ ),
435
+ ref,
436
+ style: { ...panel.factory.style, ...panel.config.style, ...sizeStyle },
437
+ children: [
438
+ panel.resizable && panelManager.config.resizeBarRender({
439
+ size,
440
+ direction: isHorizontal ? "vertical" : "horizontal",
441
+ onResize: handleResize
442
+ }),
443
+ panel.renderer
444
+ ]
445
+ },
446
+ panel.id
447
+ );
448
+ };
449
+ var PanelArea = ({ area }) => {
450
+ const panelManager = usePanelManager();
451
+ const [panels, setPanels] = useState2(panelManager.getPanels(area));
452
+ useEffect2(() => {
453
+ const dispose = panelManager.onPanelsChange(() => {
454
+ startTransition(() => {
455
+ setPanels(panelManager.getPanels(area));
456
+ });
457
+ });
458
+ return () => dispose.dispose();
459
+ }, []);
460
+ return /* @__PURE__ */ jsx2(Fragment, { children: panels.map((panel) => /* @__PURE__ */ jsx2(PanelContext.Provider, { value: panel, children: /* @__PURE__ */ jsx2(PanelItem, { panel }) }, panel.id)) });
461
+ };
462
+
264
463
  // src/components/panel-layer/css.ts
265
464
  var globalCSS = `
266
465
  .gedit-flow-panel-layer-wrap * {
@@ -279,8 +478,6 @@ var globalCSS = `
279
478
 
280
479
  }
281
480
  .gedit-flow-panel-layer-wrap-floating {
282
- column-gap: 4px;
283
- padding: 4px;
284
481
  pointer-events: none;
285
482
  }
286
483
 
@@ -292,18 +489,15 @@ var globalCSS = `
292
489
  display: flex;
293
490
  flex-direction: column;
294
491
  }
295
- .gedit-flow-panel-layer-wrap-floating .gedit-flow-panel-left-area {
296
- row-gap: 4px;
297
- }
298
492
  .gedit-flow-panel-right-area {
299
493
  height: 100%;
300
494
  flex-grow: 1;
301
495
  flex-shrink: 0;
302
496
  min-width: 0;
303
497
  display: flex;
304
- column-gap: 4px;
498
+ max-width: 100%;
305
499
  }
306
-
500
+
307
501
  .gedit-flow-panel-main-area {
308
502
  position: relative;
309
503
  overflow: hidden;
@@ -318,55 +512,18 @@ var globalCSS = `
318
512
  width: 100%;
319
513
  min-height: 0;
320
514
  }
515
+ .gedit-flow-panel-wrap {
516
+ pointer-events: auto;
517
+ overflow: auto;
518
+ position: relative;
519
+ }
520
+ .gedit-flow-panel-wrap.panel-horizontal {
521
+ height: 100%;
522
+ }
523
+ .gedit-flow-panel-wrap.panel-vertical {
524
+ width: 100%;
525
+ }
321
526
  `;
322
- var floatPanelWrap = {
323
- pointerEvents: "auto",
324
- height: "100%",
325
- width: "100%",
326
- overflow: "auto"
327
- };
328
-
329
- // src/components/panel-layer/float-panel.tsx
330
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
331
- var FloatPanel2 = ({ area }) => {
332
- const [, setVersion] = useState2(0);
333
- const panelManager = usePanelManager();
334
- const panel = useRef2(panelManager.getPanel(area));
335
- const isHorizontal = ["right", "docked-right"].includes(area);
336
- const render = () => panel.current.elements.map((i) => /* @__PURE__ */ jsx2("div", { className: "float-panel-wrap", style: { ...floatPanelWrap, ...i.style }, children: i.el }, i.key));
337
- const node = useRef2(render());
338
- useEffect2(() => {
339
- const dispose = panel.current.onUpdate(() => {
340
- startTransition(() => {
341
- node.current = render();
342
- setVersion((v) => v + 1);
343
- });
344
- });
345
- return () => dispose.dispose();
346
- }, [panel]);
347
- const onResize = useCallback((newSize) => panel.current.updateSize(newSize), []);
348
- const size = panel.current.currentSize;
349
- const sizeStyle = isHorizontal ? { width: size, height: "100%" } : { height: size, width: "100%" };
350
- return /* @__PURE__ */ jsxs(
351
- "div",
352
- {
353
- className: "gedit-flow-panel",
354
- style: {
355
- position: "relative",
356
- display: panel.current.visible ? "block" : "none",
357
- ...sizeStyle
358
- },
359
- children: [
360
- panelManager.config.resizeBarRender({
361
- size,
362
- direction: isHorizontal ? "vertical" : "horizontal",
363
- onResize
364
- }),
365
- node.current
366
- ]
367
- }
368
- );
369
- };
370
527
 
371
528
  // src/components/panel-layer/panel-layer.tsx
372
529
  import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
@@ -383,7 +540,7 @@ var PanelLayer = ({
383
540
  return /* @__PURE__ */ jsxs2(
384
541
  "div",
385
542
  {
386
- className: clsx(
543
+ className: clsx2(
387
544
  "gedit-flow-panel-layer-wrap",
388
545
  mode === "docked" && "gedit-flow-panel-layer-wrap-docked",
389
546
  mode === "floating" && "gedit-flow-panel-layer-wrap-floating",
@@ -393,9 +550,9 @@ var PanelLayer = ({
393
550
  children: [
394
551
  /* @__PURE__ */ jsxs2("div", { className: "gedit-flow-panel-left-area", children: [
395
552
  /* @__PURE__ */ jsx3("div", { className: "gedit-flow-panel-main-area", children }),
396
- /* @__PURE__ */ jsx3("div", { className: "gedit-flow-panel-bottom-area", children: /* @__PURE__ */ jsx3(FloatPanel2, { area: mode === "docked" ? "docked-bottom" : "bottom" }) })
553
+ /* @__PURE__ */ jsx3("div", { className: "gedit-flow-panel-bottom-area", children: /* @__PURE__ */ jsx3(PanelArea, { area: mode === "docked" ? "docked-bottom" : "bottom" }) })
397
554
  ] }),
398
- /* @__PURE__ */ jsx3("div", { className: "gedit-flow-panel-right-area", children: /* @__PURE__ */ jsx3(FloatPanel2, { area: mode === "docked" ? "docked-right" : "right" }) })
555
+ /* @__PURE__ */ jsx3("div", { className: "gedit-flow-panel-right-area", children: /* @__PURE__ */ jsx3(PanelArea, { area: mode === "docked" ? "docked-right" : "right" }) })
399
556
  ]
400
557
  }
401
558
  );
@@ -439,20 +596,34 @@ var PanelLayer2 = class extends Layer {
439
596
  }
440
597
  };
441
598
  __decorateClass([
442
- inject2(PanelManagerConfig)
599
+ inject3(PanelManagerConfig)
443
600
  ], PanelLayer2.prototype, "panelConfig", 2);
444
601
  __decorateClass([
445
- inject2(PluginContext)
602
+ inject3(PluginContext)
446
603
  ], PanelLayer2.prototype, "pluginContext", 2);
447
604
  PanelLayer2 = __decorateClass([
448
- injectable2()
605
+ injectable4()
449
606
  ], PanelLayer2);
450
607
 
451
608
  // src/create-panel-manager-plugin.ts
452
609
  var createPanelManagerPlugin = definePluginCreator({
453
610
  onBind: ({ bind }, opt) => {
454
611
  bind(PanelManager).to(PanelManager).inSingletonScope();
612
+ bind(PanelRestore).to(PanelRestoreImpl).inSingletonScope();
455
613
  bind(PanelManagerConfig).toConstantValue(defineConfig(opt));
614
+ bind(PanelEntityFactory).toFactory(
615
+ (context) => ({
616
+ factory,
617
+ config
618
+ }) => {
619
+ const container = context.container.createChild();
620
+ container.bind(PanelEntityFactoryConstant).toConstantValue(factory);
621
+ container.bind(PanelEntityConfigConstant).toConstantValue(config);
622
+ const panel = container.resolve(PanelEntity);
623
+ panel.init();
624
+ return panel;
625
+ }
626
+ );
456
627
  },
457
628
  onInit(ctx) {
458
629
  ctx.playground.registerLayer(PanelLayer2);
@@ -463,8 +634,10 @@ var createPanelManagerPlugin = definePluginCreator({
463
634
  export {
464
635
  DockedPanelLayer,
465
636
  PanelManager,
637
+ PanelRestore,
466
638
  ResizeBar,
467
639
  createPanelManagerPlugin,
640
+ usePanel,
468
641
  usePanelManager
469
642
  };
470
643
  //# sourceMappingURL=index.js.map