Skip to content
This repository was archived by the owner on Dec 15, 2022. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 73 additions & 19 deletions lib/atom/pane-item.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import {CompositeDisposable} from 'event-kit';

import URIPattern, {nonURIMatch} from './uri-pattern';
import RefHolder from '../models/ref-holder';
import {createItem} from '../helpers';
import StubItem from '../atom-items/stub-item';
import {createItem, autobind} from '../helpers';

/**
* PaneItem registers an opener with the current Atom workspace as long as this component is mounted. The opener will
Expand Down Expand Up @@ -37,17 +38,22 @@ export default class PaneItem extends React.Component {

constructor(props) {
super(props);
autobind(this, 'opener');

const uriPattern = new URIPattern(this.props.uriPattern);
const currentlyOpen = this.props.workspace.getPaneItems()
.map(item => {
.reduce((arr, item) => {
const element = item.getElement ? item.getElement() : null;
const match = item.getURI ? uriPattern.matches(item.getURI()) : nonURIMatch;
const stub = item.setRealItem ? item : null;
return {element, match, stub};
})
.filter(each => each.element && each.match.ok())
.map(each => new OpenItem(each.match, each.element, each.stub));

if (element && match.ok()) {
const openItem = new OpenItem(match, element, stub);
arr.push(openItem);
}

return arr;
}, []);

this.subs = new CompositeDisposable();
this.state = {uriPattern, currentlyOpen};
Expand All @@ -65,9 +71,11 @@ export default class PaneItem extends React.Component {

componentDidMount() {
for (const openItem of this.state.currentlyOpen) {
this.subs.add(this.closeListener(openItem.stubItem, openItem));
this.registerCloseListener(openItem.stubItem, openItem);

openItem.hydrateStub();
openItem.hydrateStub({
copy: () => this.copyOpenItem(openItem),
});
}

this.subs.add(this.props.workspace.addOpener(this.opener));
Expand All @@ -76,7 +84,7 @@ export default class PaneItem extends React.Component {
render() {
return this.state.currentlyOpen.map(item => {
return (
<Fragment key={item.getURI()}>
<Fragment key={item.getKey()}>
{item.renderPortal(this.props.children)}
</Fragment>
);
Expand All @@ -87,7 +95,7 @@ export default class PaneItem extends React.Component {
this.subs.dispose();
}

opener = uri => {
opener(uri) {
const m = this.state.uriPattern.matches(uri);
if (!m.ok()) {
return undefined;
Expand All @@ -99,31 +107,61 @@ export default class PaneItem extends React.Component {
this.setState(prevState => ({
currentlyOpen: [...prevState.currentlyOpen, openItem],
}), () => {
const paneItem = openItem.create();
const paneItem = openItem.create({
copy: () => this.copyOpenItem(openItem),
});
this.registerCloseListener(paneItem, openItem);
resolve(paneItem);
});
});
}

this.subs.add(this.closeListener(paneItem, openItem));
copyOpenItem(openItem) {
const m = this.state.uriPattern.matches(openItem.getURI());
if (!m.ok()) {
return null;
}

resolve(paneItem);
const stub = StubItem.create('generic', openItem.getStubProps(), openItem.getURI());

const copiedItem = new OpenItem(m, stub.getElement(), stub);
this.setState(prevState => ({
currentlyOpen: [...prevState.currentlyOpen, copiedItem],
}), () => {
this.registerCloseListener(stub, copiedItem);
copiedItem.hydrateStub({
copy: () => this.copyOpenItem(copiedItem),
});
});

return stub;
}

closeListener(paneItem, openItem) {
return this.props.workspace.onDidDestroyPaneItem(({item}) => {
registerCloseListener(paneItem, openItem) {
const sub = this.props.workspace.onDidDestroyPaneItem(({item}) => {
if (item === paneItem) {
sub.dispose();
this.subs.remove(sub);
this.setState(prevState => ({
currentlyOpen: prevState.currentlyOpen.filter(each => each !== openItem),
}));
}
});

this.subs.add(sub);
}
}

/**
* A subtree rendered through a portal onto a detached DOM node for use as the root as a PaneItem.
*/
class OpenItem {
static nextID = 0

constructor(match, element = null, stub = null) {
this.id = this.constructor.nextID;
this.constructor.nextID++;

this.domNode = element || document.createElement('div');
this.stubItem = stub;
this.match = match;
Expand All @@ -134,18 +172,34 @@ class OpenItem {
return this.match.getURI();
}

create() {
create(extra = {}) {
const h = this.itemHolder.isEmpty() ? null : this.itemHolder;
return createItem(this.domNode, h, this.match.getURI());
return createItem(this.domNode, h, this.match.getURI(), extra);
}

hydrateStub() {
hydrateStub(extra = {}) {
if (this.stubItem) {
this.stubItem.setRealItem(this.create());
this.stubItem.setRealItem(this.create(extra));
this.stubItem = null;
}
}

getKey() {
return this.id;
}

getStubProps() {
if (!this.itemHolder.isEmpty()) {
const item = this.itemHolder.get();
return {
title: item.getTitle ? item.getTitle() : null,
iconName: item.getIconName ? item.getIconName() : null,
};
} else {
return {};
}
}

renderPortal(renderProp) {
return ReactDOM.createPortal(
renderProp({
Expand Down
4 changes: 0 additions & 4 deletions lib/controllers/file-patch-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,6 @@ export default class FilePatchController extends React.Component {
};
}

copy() {
return this.props.deserializers.deserialize(this.serialize());
}

onDidDestroy(callback) {
return this.emitter.on('did-destroy', callback);
}
Expand Down
4 changes: 3 additions & 1 deletion lib/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -347,11 +347,13 @@ export function extractCoAuthorsAndRawCommitMessage(commitMessage) {
return {message: rawMessage, coAuthors};
}

export function createItem(node, componentHolder = null, uri = null) {
export function createItem(node, componentHolder = null, uri = null, extra = {}) {
const override = {
getElement: () => node,

getRealItem: () => componentHolder.get(),

...extra,
};

if (uri) {
Expand Down
14 changes: 14 additions & 0 deletions test/atom/pane-item.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,20 @@ describe('PaneItem', function() {
assert.lengthOf(wrapper.update().find('Component'), 2);
});

it('renders a different child item for each item in a different Pane', async function() {
const wrapper = mount(
<PaneItem workspace={workspace} uriPattern="atom-github://pattern/{id}">
{() => <Component text="a prop" />}
</PaneItem>,
);

const item0 = await workspace.open('atom-github://pattern/1');
const pane0 = workspace.paneForItem(item0);
pane0.splitRight({copyActiveItem: true});

assert.lengthOf(wrapper.update().find('Component'), 2);
});

it('passes matched parameters to its render prop', async function() {
let calledWith = null;
mount(
Expand Down