Skip to content

Commit 604251d

Browse files
committed
Add renderContent
1 parent 7c2f5f2 commit 604251d

File tree

7 files changed

+172
-68
lines changed

7 files changed

+172
-68
lines changed

docs/006-framework-integration.md

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# Framework integration
2+
3+
`<gem-panel>` [shadowDOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM), so it is most appropriate to use custom elements for the panel content. e.g:
4+
5+
```ts
6+
const panel = new Panel('id', {
7+
title: 'title',
8+
content: new MyElement(),
9+
});
10+
```
11+
12+
There is a disadvantage of directly specifying the content as a custom element instance, they are resident in memory, but can use lit-html template to solve this problem:
13+
14+
```ts
15+
const panel = new Panel('id', {
16+
title: 'title',
17+
content: html`<my-element></my-element>`,
18+
});
19+
```
20+
21+
If you want to use `<gem-panel>` in other frameworks, you need to use the `renderContent` factory function:
22+
23+
```ts
24+
const panel = new Panel('id', {
25+
title: 'title',
26+
async getContent(panelName) {
27+
return renderContent(panelName, (ele) => {
28+
// ...
29+
});
30+
},
31+
});
32+
```
33+
34+
## React example
35+
36+
```tsx 7
37+
import { renderContent } from 'gem-panel';
38+
// ...
39+
40+
const panel1 = new Panel('p1', {
41+
title: 'p1 title',
42+
async getContent(panelName) {
43+
return renderContent(panelName, (ele) => ReactDOM.render(<Panel1 />, ele));
44+
},
45+
});
46+
47+
// ...
48+
49+
function GemPanel({ panels, layout }) {
50+
const ref = useRef();
51+
52+
useEffect(() => {
53+
const { current } = ref;
54+
current.panels = panels;
55+
current.layout = layout;
56+
});
57+
58+
return <gem-panel ref={ref}></gem-panel>;
59+
}
60+
```
61+
62+
## Vue example
63+
64+
Binding property:
65+
66+
```html
67+
<gem-panel :panels.prop="panels" :layout.prop="layout"></gem-panel>
68+
```
69+
70+
```js 7
71+
import { renderContent } from 'gem-panel';
72+
// ...
73+
74+
const panel1 = new Panel('p1', {
75+
title: 'p1 title',
76+
async getContent(panelName) {
77+
return renderContent(panelName, (el) => new Vue({ el, components: { 'component-a': ComponentA } }));
78+
},
79+
});
80+
81+
// ...
82+
83+
export default {
84+
data: {
85+
panels: panels,
86+
layout: layout,
87+
},
88+
};
89+
```
90+
91+
Template compilation config:
92+
93+
```js
94+
{
95+
// in webpack config
96+
rules: [
97+
{
98+
test: /\.vue$/,
99+
use: 'vue-loader',
100+
options: {
101+
compilerOptions: {
102+
isCustomElement: (tag) => tag === 'gem-panel',
103+
},
104+
},
105+
},
106+
// ...
107+
];
108+
}
109+
```

docs/006-use-in-react-vue.md

Lines changed: 0 additions & 59 deletions
This file was deleted.

docs/README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ import { Layout, Panel, Window } from 'gem-panel';
2727

2828
const panel1 = new Panel('p1', { title: 'p1 title', content: `p1 content` });
2929
const panel2 = new Panel('p2', { title: 'p2 title' });
30-
const panel3 = new Panel('p3', { title: 'p3 title', content: `<p3-panel></p3-panel>` });
31-
const panel4 = new Panel('p4', { title: 'p4 title', content: `<p4-panel></p4-panel>` });
32-
const panel5 = new Panel('p5', { title: 'p5 title', content: `<p5-panel></p5-panel>` });
33-
const panel6 = new Panel('p6', { title: 'p6 title', content: `<p6-panel></p6-panel>` });
30+
const panel3 = new Panel('p3', { title: 'p3 title', content: `p3-content` });
31+
const panel4 = new Panel('p4', { title: 'p4 title', content: `p4-content` });
32+
const panel5 = new Panel('p5', { title: 'p5 title', content: `p5-content` });
33+
const panel6 = new Panel('p6', { title: 'p6 title', content: `p6-content` });
3434

3535
const panels = [panel1, panel2, panel3, panel4, panel5, panel6];
3636
```

src/elements/root.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,11 @@ export class GemPanelElement extends GemElement {
8686
resizeObserver.observe(this);
8787
};
8888

89+
#renderPanelSlot = (window: Window) => {
90+
const panel = store.panels[window.panels[window.current]];
91+
return html`<slot slot=${panel.name} name=${panel.name}></slot>`;
92+
};
93+
8994
mounted = () => {
9095
this.#onResize();
9196

@@ -160,7 +165,9 @@ export class GemPanelElement extends GemElement {
160165
panel-loader
161166
"
162167
.window=${window}
163-
></gem-panel-window>
168+
>
169+
${this.#renderPanelSlot(window)}
170+
</gem-panel-window>
164171
`,
165172
)}
166173
<gem-panel-menu

src/elements/window.ts

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { PanEventDetail } from '@mantou/gem/elements/gesture';
33
import '@mantou/gem/elements/gesture';
44

55
import { Window } from '../lib/layout';
6-
import { GetPanelContent } from '../lib/panel';
6+
import { GetPanelContent, Panel } from '../lib/panel';
77
import {
88
cancelHandleWindow,
99
dropHandleWindow,
@@ -16,6 +16,7 @@ import {
1616
updateWindowZIndex,
1717
independentPanel,
1818
loadContentInPanel,
19+
unLoadContentInPanel,
1920
} from '../lib/store';
2021
import { GemPanelTitleElement } from './panel-title';
2122
import { distance } from '../lib/utils';
@@ -44,6 +45,23 @@ function execGetContent(fn: GetPanelContent | undefined, panelName: string) {
4445
}
4546
}
4647

48+
const renderContentWeakMap = new WeakMap<HTMLSlotElement, HTMLDivElement>();
49+
export function renderContent(
50+
panelName: string,
51+
render: (contentMountElement: HTMLDivElement) => void,
52+
gemPanelRootElement: ParentNode = document,
53+
) {
54+
const contentMountElement = document.createElement('div');
55+
contentMountElement.setAttribute('slot', panelName);
56+
contentMountElement.setAttribute('style', 'display: contents');
57+
render(contentMountElement);
58+
gemPanelRootElement.querySelector<any>('gem-panel').append(contentMountElement);
59+
const slot = document.createElement('slot');
60+
slot.setAttribute('name', panelName);
61+
renderContentWeakMap.set(slot, contentMountElement);
62+
return slot;
63+
}
64+
4765
export const windowTagName = 'gem-panel-window';
4866

4967
type State = {
@@ -205,16 +223,37 @@ export class GemPanelWindowElement extends GemElement<State> {
205223
updateWindowZIndex(this.window);
206224
};
207225

226+
#removeSlotAssignContent = (panel: Panel | undefined) => {
227+
if (panel) {
228+
const { content, name, getContent } = panel;
229+
if (!getContent) return;
230+
231+
const slotAssignContent = renderContentWeakMap.get(content as any);
232+
if (slotAssignContent) {
233+
unLoadContentInPanel(name);
234+
// The next rendering is to use `getContent`
235+
getContentWeakMap.delete(getContent);
236+
slotAssignContent.remove();
237+
}
238+
}
239+
};
240+
208241
mounted = () => {
209242
this.addEventListener('focus', this.#onFocusWindow);
210243
this.addEventListener('pointermove', this.#onMove);
211244
this.addEventListener('pointerup', this.#onMoveEnd);
212245
this.addEventListener('pointercancel', this.#onMoveEnd);
213246
this.effect(
214-
([currentPanel]) => {
215-
if (currentPanel && !currentPanel.content) {
216-
execGetContent(currentPanel.getContent, currentPanel.name);
247+
([currentPanel], oldDepValues) => {
248+
if (!currentPanel) return;
249+
const { content, getContent, name } = currentPanel;
250+
if (!content) {
251+
execGetContent(getContent, name);
217252
}
253+
this.#removeSlotAssignContent(oldDepValues?.[0]);
254+
return () => {
255+
this.#removeSlotAssignContent(currentPanel);
256+
};
218257
},
219258
() => {
220259
const { panels, current } = this.window;

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
export { html } from '@mantou/gem';
12
export { GemPanelElement } from './elements/root';
3+
export { renderContent } from './elements/window';
24
export { MenuItem } from './elements/menu';
35
export { Window, Layout } from './lib/layout';
46
export { Panel } from './lib/panel';

src/lib/store.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@ export function loadContentInPanel(panelName: string, content: PanelContent) {
6565
updateStore(store);
6666
}
6767

68+
export function unLoadContentInPanel(panelName: string) {
69+
const panel = store.panels[panelName];
70+
if (!panel) return;
71+
delete panel.detail.content;
72+
}
73+
6874
export function independentPanel(window: Window, panelName: string, rect: [number, number, number, number]) {
6975
const newWindow = store.layout.createIndependentWindow(window, panelName, rect);
7076
updateStore(store);

0 commit comments

Comments
 (0)