Dialog management cleanup
Summary: This diff moves the dialogs * Settings * Plugin Manager * Doctor * Sign in * Changelog To use the imperative dialog APIs, rather then organising them through reducers which adds a lot of indirection which isn't really needed but hard to follow. Reviewed By: passy Differential Revision: D30192002 fbshipit-source-id: ba38b2e700da3e442653786448fcbf85074981ad
This commit is contained in:
committed by
Facebook GitHub Bot
parent
89b193b438
commit
9e5575cf69
@@ -85,6 +85,7 @@ test('Correct top level API exposed', () => {
|
||||
"DeviceLogEntry",
|
||||
"DeviceLogListener",
|
||||
"DevicePluginClient",
|
||||
"DialogResult",
|
||||
"Draft",
|
||||
"ElementAttribute",
|
||||
"ElementData",
|
||||
|
||||
@@ -61,7 +61,7 @@ export {Toolbar} from './ui/Toolbar';
|
||||
export {MasterDetail} from './ui/MasterDetail';
|
||||
export {CodeBlock} from './ui/CodeBlock';
|
||||
|
||||
export {renderReactRoot} from './utils/renderReactRoot';
|
||||
export {renderReactRoot, _PortalsManager} from './utils/renderReactRoot';
|
||||
export {
|
||||
Tracked,
|
||||
TrackingScope,
|
||||
@@ -115,7 +115,7 @@ export {
|
||||
} from './ui/data-inspector/DataDescription';
|
||||
export {MarkerTimeline} from './ui/MarkerTimeline';
|
||||
export {DataInspector} from './ui/data-inspector/DataInspector';
|
||||
export {Dialog} from './ui/Dialog';
|
||||
export {Dialog, DialogResult} from './ui/Dialog';
|
||||
export {
|
||||
ElementsInspector,
|
||||
Element as ElementsInspectorElement,
|
||||
|
||||
@@ -13,7 +13,8 @@ import React from 'react';
|
||||
import {renderReactRoot} from '../utils/renderReactRoot';
|
||||
import {Layout} from './Layout';
|
||||
import {Spinner} from './Spinner';
|
||||
type DialogResult<T> = Promise<false | T> & {close: () => void};
|
||||
|
||||
export type DialogResult<T> = Promise<false | T> & {close: () => void};
|
||||
|
||||
type BaseDialogOptions = {
|
||||
title: string;
|
||||
@@ -104,6 +105,36 @@ export const Dialog = {
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows an item in the modal stack, but without providing any further UI, like .show does.
|
||||
*/
|
||||
showModal<T = void>(
|
||||
fn: (hide: (result?: T) => void) => React.ReactElement,
|
||||
): DialogResult<T> {
|
||||
let cancel: () => void;
|
||||
|
||||
return Object.assign(
|
||||
new Promise<false | T>((resolve) => {
|
||||
renderReactRoot((hide) => {
|
||||
cancel = () => {
|
||||
hide();
|
||||
resolve(false);
|
||||
};
|
||||
|
||||
return fn((result?: T) => {
|
||||
hide();
|
||||
resolve(result ?? false);
|
||||
});
|
||||
});
|
||||
}),
|
||||
{
|
||||
close() {
|
||||
cancel();
|
||||
},
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
confirm({
|
||||
message,
|
||||
onConfirm,
|
||||
|
||||
@@ -7,8 +7,9 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {render, unmountComponentAtNode} from 'react-dom';
|
||||
import {createState, useValue} from '../state/atom';
|
||||
import React, {ReactPortal} from 'react';
|
||||
import {createPortal, unmountComponentAtNode} from 'react-dom';
|
||||
|
||||
/**
|
||||
* This utility creates a fresh react render hook, which is great to render elements imperatively, like opening dialogs.
|
||||
@@ -20,9 +21,28 @@ export function renderReactRoot(
|
||||
// TODO: find a way to make this visible in unit tests as well
|
||||
const div = document.body.appendChild(document.createElement('div'));
|
||||
const unmount = () => {
|
||||
portals.update((draft) => {
|
||||
draft.delete(id);
|
||||
});
|
||||
unmountComponentAtNode(div);
|
||||
div.remove();
|
||||
};
|
||||
render(handler(unmount), div);
|
||||
const id = ++portalId;
|
||||
const portal = createPortal(handler(unmount), div);
|
||||
portals.update((draft) => {
|
||||
draft.set(id, portal);
|
||||
});
|
||||
|
||||
return unmount;
|
||||
}
|
||||
|
||||
let portalId = 0;
|
||||
const portals = createState(new Map<number, ReactPortal>());
|
||||
|
||||
/**
|
||||
* This is a dummy component, that just makes sure react roots are managed within a certain node in the main React tree, so that context etc is available.
|
||||
*/
|
||||
export function _PortalsManager() {
|
||||
const portalElements = useValue(portals);
|
||||
return <>{Array.from(portalElements).map(([_id, portal]) => portal)}</>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user