94 | );
95 | ```
96 |
97 | `renderTile` is a stateless lookup function to convert `T` into a displayable `JSX.Element`.
98 | By default `T` is `string` (so to render one element `initialValue="ID"` works).
99 | `T`s must be unique within an instance of `Mosaic`, they are used as keys for [React list management](https://reactjs.org/docs/lists-and-keys.html).
100 | `initialValue` is a [`MosaicNode`](./src/types.ts#L12).
101 |
102 | The user can resize these panes but there is no other advanced functionality.
103 | This example renders a simple tiled interface with one element on the left half, and two stacked elements on the right half.
104 | The user can resize these panes but there is no other advanced functionality.
105 |
106 | #### Drag, Drop, and other advanced functionality with `MosaicWindow`
107 |
108 | `MosaicWindow` is a component that renders a toolbar and controls around its children for a tile as well as providing full featured drag and drop functionality.
109 |
110 | ```tsx
111 | export type ViewId = 'a' | 'b' | 'c' | 'new';
112 |
113 | const TITLE_MAP: Record = {
114 | a: 'Left Window',
115 | b: 'Top Right Window',
116 | c: 'Bottom Right Window',
117 | new: 'New Window',
118 | };
119 |
120 | export const app = (
121 |
122 | renderTile={(id, path) => (
123 | path={path} createNode={() => 'new'} title={TITLE_MAP[id]}>
124 |
{TITLE_MAP[id]}
125 |
126 | )}
127 | initialValue={{
128 | direction: 'row',
129 | first: 'a',
130 | second: {
131 | direction: 'column',
132 | first: 'b',
133 | second: 'c',
134 | },
135 | }}
136 | />
137 | );
138 | ```
139 |
140 | Here `T` is a `ViewId` that can be used to look elements up in `TITLE_MAP`.
141 | This allows for easy view state specification and serialization.
142 | This will render a view that looks very similar to the previous examples, but now each of the windows will have a toolbar with buttons.
143 | These toolbars can be dragged around by a user to rearrange their workspace.
144 |
145 | `MosaicWindow` API docs [here](#mosaicwindow).
146 |
147 | #### Controlled vs. Uncontrolled
148 |
149 | Mosaic views have two modes, similar to `React.DOM` input elements:
150 |
151 | - Controlled, where the consumer manages Mosaic's state through callbacks.
152 | Using this API, the consumer can perform any operation upon the tree to change the the view as desired.
153 | - Uncontrolled, where Mosaic manages all of its state internally.
154 |
155 | See [Controlled Components](https://facebook.github.io/react/docs/forms.html#controlled-components).
156 |
157 | All of the previous examples show use of Mosaic in an Uncontrolled fashion.
158 |
159 | #### Example Application
160 |
161 | See [ExampleApp](demo/ExampleApp.tsx) (the application used in the [Demo](https://nomcopter.github.io/react-mosaic/))
162 | for a more interesting example that shows the usage of Mosaic as a controlled component and modifications of the tree structure.
163 |
164 | ## API
165 |
166 | #### Mosaic Props
167 |
168 | ```typescript
169 | export interface MosaicBaseProps {
170 | /**
171 | * Lookup function to convert `T` to a displayable `JSX.Element`
172 | */
173 | renderTile: TileRenderer;
174 | /**
175 | * Called when a user initiates any change to the tree (removing, adding, moving, resizing, etc.)
176 | */
177 | onChange?: (newNode: MosaicNode | null) => void;
178 | /**
179 | * Called when a user completes a change (fires like above except for the interpolation during resizing)
180 | */
181 | onRelease?: (newNode: MosaicNode | null) => void;
182 | /**
183 | * Additional classes to affix to the root element
184 | * Default: 'mosaic-blueprint-theme'
185 | */
186 | className?: string;
187 | /**
188 | * Options that control resizing
189 | * @see: [[ResizeOptions]]
190 | */
191 | resize?: ResizeOptions;
192 | /**
193 | * View to display when the current value is `null`
194 | * default: Simple NonIdealState view
195 | */
196 | zeroStateView?: JSX.Element;
197 | /**
198 | * Override the mosaicId passed to `react-dnd` to control how drag and drop works with other components
199 | * Note: does not support updating after instantiation
200 | * default: Random UUID
201 | */
202 | mosaicId?: string;
203 | /**
204 | * Make it possible to use different versions of Blueprint with `mosaic-blueprint-theme`
205 | * Note: does not support updating after instantiation
206 | * default: 'bp3'
207 | */
208 | blueprintNamespace?: string;
209 | /**
210 | * Override the react-dnd provider to allow applications to inject an existing drag and drop context
211 | */
212 | dragAndDropManager?: DragDropManager | undefined;
213 | }
214 |
215 | export interface MosaicControlledProps extends MosaicBaseProps {
216 | /**
217 | * The tree to render
218 | */
219 | value: MosaicNode | null;
220 | onChange: (newNode: MosaicNode | null) => void;
221 | }
222 |
223 | export interface MosaicUncontrolledProps extends MosaicBaseProps {
224 | /**
225 | * The initial tree to render, can be modified by the user
226 | */
227 | initialValue: MosaicNode | null;
228 | }
229 |
230 | export type MosaicProps = MosaicControlledProps | MosaicUncontrolledProps;
231 | ```
232 |
233 | #### `MosaicWindow`
234 |
235 | ```typescript
236 | export interface MosaicWindowProps {
237 | title: string;
238 | /**
239 | * Current path to this window, provided by `renderTile`
240 | */
241 | path: MosaicBranch[];
242 | className?: string;
243 | /**
244 | * Controls in the top right of the toolbar
245 | * default: [Replace, Split, Expand, Remove] if createNode is defined and [Expand, Remove] otherwise
246 | */
247 | toolbarControls?: React.ReactNode;
248 | /**
249 | * Additional controls that will be hidden in a drawer beneath the toolbar.
250 | * default: []
251 | */
252 | additionalControls?: React.ReactNode;
253 | /**
254 | * Label for the button that expands the drawer
255 | */
256 | additionalControlButtonText?: string;
257 | /**
258 | * A callback that triggers when a user toggles the additional controls
259 | */
260 | onAdditionalControlsToggle?: (toggle: boolean) => void;
261 | /**
262 | * Disables the overlay that blocks interaction with the window when additional controls are open
263 | */
264 | disableAdditionalControlsOverlay?: boolean;
265 | /**
266 | * Whether or not a user should be able to drag windows around
267 | */
268 | draggable?: boolean;
269 | /**
270 | * Method called when a new node is required (such as the Split or Replace buttons)
271 | */
272 | createNode?: CreateNode;
273 | /**
274 | * Optional method to override the displayed preview when a user drags a window
275 | */
276 | renderPreview?: (props: MosaicWindowProps) => JSX.Element;
277 | /**
278 | * Optional method to override the displayed toolbar
279 | */
280 | renderToolbar?: ((props: MosaicWindowProps, draggable: boolean | undefined) => JSX.Element) | null;
281 | /**
282 | * Optional listener for when the user begins dragging the window
283 | */
284 | onDragStart?: () => void;
285 | /**
286 | * Optional listener for when the user finishes dragging a window.
287 | */
288 | onDragEnd?: (type: 'drop' | 'reset') => void;
289 | }
290 | ```
291 |
292 | The default controls rendered by `MosaicWindow` can be accessed from [`defaultToolbarControls`](./src/buttons/defaultToolbarControls.tsx)
293 |
294 | ### Advanced API
295 |
296 | The above API is good for most consumers, however Mosaic provides functionality on the [Context](https://facebook.github.io/react/docs/context.html) of its children that make it easier to alter the view state.
297 | All leaves rendered by Mosaic will have the following available on React context.
298 | These are used extensively by `MosaicWindow`.
299 |
300 | ```typescript
301 | /**
302 | * Valid node types
303 | * @see React.Key
304 | */
305 | export type MosaicKey = string | number;
306 | export type MosaicBranch = 'first' | 'second';
307 | export type MosaicPath = MosaicBranch[];
308 |
309 | /**
310 | * Context provided to everything within Mosaic
311 | */
312 | export interface MosaicContext {
313 | mosaicActions: MosaicRootActions;
314 | mosaicId: string;
315 | }
316 |
317 | export interface MosaicRootActions {
318 | /**
319 | * Increases the size of this node and bubbles up the tree
320 | * @param path Path to node to expand
321 | * @param percentage Every node in the path up to root will be expanded to this percentage
322 | */
323 | expand: (path: MosaicPath, percentage?: number) => void;
324 | /**
325 | * Remove the node at `path`
326 | * @param path
327 | */
328 | remove: (path: MosaicPath) => void;
329 | /**
330 | * Hide the node at `path` but keep it in the DOM. Used in Drag and Drop
331 | * @param path
332 | */
333 | hide: (path: MosaicPath) => void;
334 | /**
335 | * Replace currentNode at `path` with `node`
336 | * @param path
337 | * @param node
338 | */
339 | replaceWith: (path: MosaicPath, node: MosaicNode) => void;
340 | /**
341 | * Atomically applies all updates to the current tree
342 | * @param updates
343 | * @param suppressOnRelease (default: false)
344 | */
345 | updateTree: (updates: MosaicUpdate[], suppressOnRelease?: boolean) => void;
346 | /**
347 | * Returns the root of this Mosaic instance
348 | */
349 | getRoot: () => MosaicNode | null;
350 | }
351 | ```
352 |
353 | Children (and toolbar elements) within `MosaicWindow` are passed the following additional functions on context.
354 |
355 | ```typescript
356 | export interface MosaicWindowContext extends MosaicContext {
357 | mosaicWindowActions: MosaicWindowActions;
358 | }
359 |
360 | export interface MosaicWindowActions {
361 | /**
362 | * Fails if no `createNode()` is defined
363 | * Creates a new node and splits the current node.
364 | * The current node becomes the `first` and the new node the `second` of the result.
365 | * `direction` is chosen by querying the DOM and splitting along the longer axis
366 | */
367 | split: () => Promise;
368 | /**
369 | * Fails if no `createNode()` is defined
370 | * Convenience function to call `createNode()` and replace the current node with it.
371 | */
372 | replaceWithNew: () => Promise;
373 | /**
374 | * Sets the open state for the tray that holds additional controls.
375 | * Pass 'toggle' to invert the current state.
376 | */
377 | setAdditionalControlsOpen: (open: boolean | 'toggle') => void;
378 | /**
379 | * Returns the path to this window
380 | */
381 | getPath: () => MosaicPath;
382 | /**
383 | * Enables connecting a different drag source besides the react-mosaic toolbar
384 | */
385 | connectDragSource: (connectedElements: React.ReactElement) => React.ReactElement;
386 | }
387 | ```
388 |
389 | To access the functions simply use the [`MosaicContext`](./src/contextTypes.ts#L90)
390 | or [`MosaicWindowContext`](./src/contextTypes.ts#L91) [context consumers](https://reactjs.org/docs/context.html#contextconsumer).
391 |
392 | ### Mutating the Tree
393 |
394 | Utilities are provided for working with the MosaicNode tree in [`mosaicUtilities`](src/util/mosaicUtilities.ts) and
395 | [`mosaicUpdates`](src/util/mosaicUpdates.ts)
396 |
397 | #### MosaicUpdate
398 |
399 | [`MosaicUpdateSpec`](./src/types.ts#L33) is an argument meant to be passed to [`immutability-helper`](https://github.com/kolodny/immutability-helper)
400 | to modify the state at a path.
401 | [`mosaicUpdates`](src/util/mosaicUpdates.ts) has examples.
402 |
403 | ## Upgrade Considerations / Changelog
404 |
405 | See [Releases](https://github.com/nomcopter/react-mosaic/releases)
406 |
407 | ## License
408 |
409 | Copyright 2019 Kevin Verdieck, originally developed at Palantir Technologies, Inc.
410 |
411 | Licensed under the Apache License, Version 2.0 (the "License");
412 | you may not use this file except in compliance with the License.
413 | You may obtain a copy of the License at
414 |
415 | http://www.apache.org/licenses/LICENSE-2.0
416 |
417 | Unless required by applicable law or agreed to in writing, software
418 | distributed under the License is distributed on an "AS IS" BASIS,
419 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
420 | See the License for the specific language governing permissions and
421 | limitations under the License.
422 |
--------------------------------------------------------------------------------