Skip to content
Open
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
4 changes: 2 additions & 2 deletions demo/ExampleApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export const THEMES = {

export type Theme = keyof typeof THEMES;

const additionalControls = React.Children.toArray([<CloseAdditionalControlsButton />]);
const additionalControls = React.Children.toArray([<CloseAdditionalControlsButton key={'key'} />]);

const EMPTY_ARRAY: any[] = [];

Expand Down Expand Up @@ -161,7 +161,7 @@ export class ExampleApp extends React.PureComponent<{}, ExampleAppState> {
value={this.state.currentTheme}
onChange={(e) => this.setState({ currentTheme: e.currentTarget.value as Theme })}
>
{React.Children.toArray(Object.keys(THEMES).map((label) => <option>{label}</option>))}
{React.Children.toArray(Object.keys(THEMES).map((label) => <option key={label}>{label}</option>))}
</HTMLSelect>
</label>
<div className="navbar-separator" />
Expand Down
12 changes: 5 additions & 7 deletions demo/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { createRoot } from 'react-dom/client';
import { ExampleApp } from './ExampleApp';

const APP_ELEMENT = document.getElementById('app')!;
const render = (Component: React.ComponentClass<any>) => {
ReactDOM.render(<Component />, APP_ELEMENT);
};

render(ExampleApp);
const APP_ELEMENT = document.getElementById('app');
if (APP_ELEMENT) {
createRoot(APP_ELEMENT).render(<ExampleApp />);
}
16 changes: 9 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
"description": "A React Tiling Window Manager",
"license": "Apache-2.0",
"main": "lib/index.js",
"module": "lib/index.js",
"style": "lib/react-mosaic.css",
"type": "commonjs",
"typings": "lib/index.d.ts",
"repository": {
"type": "git",
Expand Down Expand Up @@ -38,17 +38,19 @@
"fix": "npm-run-all -lp fix:**",
"fix:format": "yarn run prettier:run --write",
"fix:lint": "yarn run test:lint --fix",
"version": "npm-run-all test bundle && git add -A docs/"
"version": "npm-run-all test bundle && git add -A docs/",
"prepare": "yarn run build"
},
"dependencies": {
"classnames": "^2.3.2",
"core-js": "^3.48.0",
"immutability-helper": "^3.1.1",
"lodash": "^4.17.21",
"prop-types": "^15.8.1",
"rdndmb-html5-to-touch": "^8.0.0",
"react-dnd": "^16.0.1",
"react-dnd-html5-backend": "^16.0.1",
"react-dnd-multi-backend": "^8.0.0",
"react-dnd-multi-backend": "^9.0.0",
"react-dnd-touch-backend": "^16.0.1",
"uuid": "^9.0.0"
},
Expand All @@ -62,8 +64,8 @@
"@types/lodash": "^4.14.191",
"@types/mocha": "^7.0.2",
"@types/prop-types": "^15.7.5",
"@types/react": "^18.0.28",
"@types/react-dom": "^18.0.10",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"@types/uuid": "^9.0.0",
"@types/webpack": "^5.28.0",
"chai": "^4.3.6",
Expand All @@ -81,8 +83,8 @@
"mock-require": "^3.0.3",
"npm-run-all": "^4.1.5",
"prettier": "^2.8.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-refresh": "^0.14.0",
"source-map-loader": "^4.0.1",
"style-loader": "^3.3.1",
Expand Down
2 changes: 1 addition & 1 deletion src/Mosaic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import countBy from 'lodash/countBy';
import keys from 'lodash/keys';
import pickBy from 'lodash/pickBy';
import { HTML5toTouch } from 'rdndmb-html5-to-touch';
import React from 'react';
import React, { JSX } from 'react';
import { DndProvider } from 'react-dnd';
import { MultiBackend } from 'react-dnd-multi-backend';
import { v4 as uuid } from 'uuid';
Expand Down
2 changes: 1 addition & 1 deletion src/MosaicDropTarget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export function MosaicDropTarget({ path, position }: MosaicDropTargetProps) {
});
return (
<div
ref={connectDropTarget}
ref={connectDropTarget as unknown as React.Ref<HTMLDivElement> | undefined}
className={classNames('drop-target', position, {
'drop-target-hover': isOver && draggedMosaicId === mosaicId,
})}
Expand Down
2 changes: 1 addition & 1 deletion src/MosaicRoot.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import flatten from 'lodash/flatten';
import React from 'react';
import React, { JSX } from 'react';
import { MosaicContext } from './contextTypes';
import { Split } from './Split';
import { MosaicBranch, MosaicDirection, MosaicKey, MosaicNode, ResizeOptions, TileRenderer } from './types';
Expand Down
83 changes: 49 additions & 34 deletions src/MosaicWindow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import dropRight from 'lodash/dropRight';
import isEmpty from 'lodash/isEmpty';
import isEqual from 'lodash/isEqual';
import values from 'lodash/values';
import React, { useContext } from 'react';
import React, { JSX, useContext } from 'react';
import {
ConnectDragPreview,
ConnectDragSource,
Expand Down Expand Up @@ -104,31 +104,32 @@ export class InternalMosaicWindow<T extends MosaicKey> extends React.Component<

return (
<MosaicWindowContext.Provider value={this.childContext}>
{connectDropTarget(
<div
className={classNames('mosaic-window mosaic-drop-target', className, {
'drop-target-hover': isOver && draggedMosaicId === this.context.mosaicId,
'additional-controls-open': this.state.additionalControlsOpen,
})}
ref={(element) => (this.rootElement = element)}
>
{this.renderToolbar()}
<div className="mosaic-window-body">{this.props.children}</div>
{!disableAdditionalControlsOverlay && (
<div
className="mosaic-window-body-overlay"
onClick={() => {
this.setAdditionalControlsOpen(false);
}}
/>
)}
<div className="mosaic-window-additional-actions-bar">{additionalControls}</div>
{connectDragPreview(renderPreview!(this.props))}
<div className="drop-target-container">
{values<MosaicDropTargetPosition>(MosaicDropTargetPosition).map(this.renderDropTarget)}
</div>
</div>,
)}
<div
className={classNames('mosaic-window mosaic-drop-target', className, {
'drop-target-hover': isOver && draggedMosaicId === this.context.mosaicId,
'additional-controls-open': this.state.additionalControlsOpen,
})}
ref={(element) => {
this.rootElement = element;
connectDropTarget(element);
}}
>
{this.renderToolbar()}
<div className="mosaic-window-body">{this.props.children}</div>
{!disableAdditionalControlsOverlay && (
<div
className="mosaic-window-body-overlay"
onClick={() => {
this.setAdditionalControlsOpen(false);
}}
/>
)}
<div className="mosaic-window-additional-actions-bar">{additionalControls}</div>
<div ref={(element) => connectDragPreview(element) as any}>{renderPreview!(this.props)}</div>
<div className="drop-target-container">
{values<MosaicDropTargetPosition>(MosaicDropTargetPosition).map(this.renderDropTarget)}
</div>
</div>
</MosaicWindowContext.Provider>
);
}
Expand All @@ -145,26 +146,38 @@ export class InternalMosaicWindow<T extends MosaicKey> extends React.Component<
}

private renderToolbar() {
const { title, draggable, additionalControls, additionalControlButtonText, path, renderToolbar } = this.props;
const {
title,
draggable,
additionalControls,
additionalControlButtonText,
path,
renderToolbar,
connectDragSource,
} = this.props;
const { additionalControlsOpen } = this.state;
const toolbarControls = this.getToolbarControls();
const draggableAndNotRoot = draggable && path.length > 0;
const connectIfDraggable = draggableAndNotRoot ? this.props.connectDragSource : (el: React.ReactElement) => el;

if (renderToolbar) {
const connectedToolbar = connectIfDraggable(renderToolbar(this.props, draggable)) as React.ReactElement<any>;
return (
<div className={classNames('mosaic-window-toolbar', { draggable: draggableAndNotRoot })}>
{connectedToolbar}
<div ref={draggableAndNotRoot ? (el) => connectDragSource(el) as any : undefined}>
{renderToolbar(this.props, draggable)}
</div>
</div>
);
}

const titleDiv = connectIfDraggable(
<div title={title} className="mosaic-window-title">
const titleDiv = (
<div
title={title}
className="mosaic-window-title"
ref={draggableAndNotRoot ? (el) => connectDragSource(el) as any : undefined}
>
{title}
</div>,
)!;
</div>
);

const hasAdditionalControls = !isEmpty(additionalControls);

Expand Down Expand Up @@ -241,6 +254,8 @@ export class InternalMosaicWindow<T extends MosaicKey> extends React.Component<

private connectDragSource = (connectedElements: React.ReactElement<any>) => {
const { connectDragSource } = this.props;
// In React 19, element.ref is removed. Pass through for backward compat
// but prefer using callback refs directly.
return connectDragSource(connectedElements);
};

Expand Down
13 changes: 8 additions & 5 deletions src/buttons/defaultToolbarControls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ import { ReplaceButton } from './ReplaceButton';
import { SplitButton } from './SplitButton';

export const DEFAULT_CONTROLS_WITH_CREATION = React.Children.toArray([
<ReplaceButton />,
<SplitButton />,
<ExpandButton />,
<RemoveButton />,
<ReplaceButton key="replace" />,
<SplitButton key="split" />,
<ExpandButton key="expand" />,
<RemoveButton key="remove" />,
]);
export const DEFAULT_CONTROLS_WITHOUT_CREATION = React.Children.toArray([
<ExpandButton key="expand" />,
<RemoveButton key="remove" />,
]);
export const DEFAULT_CONTROLS_WITHOUT_CREATION = React.Children.toArray([<ExpandButton />, <RemoveButton />]);
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Spec } from 'immutability-helper';
import { JSX } from 'react';

/**
* Valid node types
Expand Down
7 changes: 5 additions & 2 deletions tsconfig-build.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
"compilerOptions": {
"noEmit": false,
"outDir": "lib",
"rootDir": "src"
"rootDir": "src",
"module": "commonjs",
"target": "es2015",
"moduleResolution": "node"
},
"exclude": [
"demo",
Expand All @@ -13,4 +16,4 @@
"webpack"
],
"extends": "./tsconfig.json"
}
}
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@
"strict": true
},
"exclude": ["docs", "lib", "node_modules"],
}
}
5 changes: 5 additions & 0 deletions webpack/hot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ const hotConfig: webpack.Configuration = {
hot: true,
host: '0.0.0.0',
port: CONSTANTS.DEV_SERVER_PORT,

client: {
// Disable overlay for showing the blueprintjs errors of not supporting React 19
overlay: false,
},
},
plugins: [...(config.plugins || []), new ReactRefreshWebpackPlugin()],
};
Expand Down
Loading