node-projects / dock-spawn-ts

A TypeScript HTML Docking Framework (fork of dock-spawn)
MIT License
127 stars 28 forks source link

Better documentation on how to use it from NPM #8

Closed robertdumitrescu closed 4 years ago

robertdumitrescu commented 5 years ago

We need better documentation on how to install it from NPM and how to use it after doing so.

jogibear9988 commented 5 years ago

@robertdumitrescu could you work on this?

one main issue also could be, that it is not transpiled to es5.

maybe we should create an extra es5 npm package (i will not change the main one, cause we use completely es6 with modules in or company, without transpiling or packaging)

robertdumitrescu commented 5 years ago

Maybe is worth having 2 folders like lib (for ES6) and lib-legacy (ES5) and to be up to the implementer which one to choose to implement

jogibear9988 commented 5 years ago

good idea, I think we should create a new legacy folder, wich contains a es5 module wich could be required. And also the es6 part could be changed so only one import is needed, not one for every class

jogibear9988 commented 5 years ago

I also think we should use a CI system like appveyor to compile typescript (and create es5 code). Needs work

dinobot71 commented 4 years ago

hi, would like to add my vote to this...using it with Angular 9 and its a big help, thanks for this project! :) But learning curve requires looking at the source a bit :( Would be nice to have some more examples and stuff. I might be able to find hours to chip in if required. I have a couple of usage scenario questions...but I'll open a separate ticket.

Also resizing internally with the pane drag sliders or closing windows seems to leave gaps until you drag the browser and force a resize...again I'll open a separate issue.

jogibear9988 commented 4 years ago

@dinobot71 maybe you help out with this? I don't use angular, so I could not help really in how to use with it.

dinobot71 commented 4 years ago

for sure I can provide examples of how I integrated; the main concept is that dock spawn windows are created dynamically...so if you create Angular components inside them...they must also be dynamically created...there is some specific code needed for that. I can post an example here, stay tuned...

dinobot71 commented 4 years ago

Angular integration

Some notes on integrating Dock Spawn TS into an Angular Project

Project Configuration

NPM Setup

Add the module to your project:

npm i --save dock-spawn-ts

angular.json, package.json etc.

No updates, the classes are simply directly imported into any Angular components where you are are working with Dock Spawn TS.

style.css

The Dock Spawn TS CSS rules must be imported, this can easily by done by adding an @import to the style.css file:

@import "../node_modules/dock-spawn-ts/lib/css/dock-manager.css"; @import "../node_modules/dock-spawn-ts/lib/css/dock-manager-style.css";

Component Template Updates

Your angular application will have route such as / or /ide where you are are using Dock Spawn TS along with menu bars etc to present your main user interface. That component will need have its template updated to include <divs> for Dock Spawn TS to use, and that component will have to import and use Dock Spawn TS classes to actually setup those which are used to host the Dock Spawn TS windows.

The template for that main GUI component should include the that will be used to host the panels for Dock Spawn TS. For example, if your main GUI component is named MainPageComponent, you might have a file main.page.component.html template like:

...
<div id="dock_div" >  

  <div id="my_dock_manager" class="my-dock-manager" style="position: relative;">  
  </div>  

  <div id="solution_window"  
  class="solution-window window-panel"  
  hidden>  
    <div id="projecttree" style="overflow: hidden;"></div>  
  </div>
  ...

Your component template must include enough of the Dock Spawn TS style <div> elements to create panels as needed (similar to the example on the Dock Spawn TS homepage).

Component Updates

Continuing the above example, you would also have a main.page.component.ts file, with the Angular code for your main page, is must be updated to actually setup the <divs> to be Dock Span TS panels.

Importing

A minimum set of imports might be:

import { DockNode }          from 'dock-spawn-ts/lib/js/DockNode';  
import { DockManager }       from 'dock-spawn-ts/lib/js/DockManager';  
import { PanelContainer }    from 'dock-spawn-ts/lib/js/PanelContainer';  
import { DockConfig }        from 'dock-spawn-ts/lib/js/DockConfig';

If your component uses more advanced features, you may need more imports! To get started though, these are enough to create panels similar to the example on the Dock Spawn TS home page.

The HTML template for the component is rendered (DOM created) by the time your component's ngInit() or ngAfterViewInit() methods are called. You can easily setup Dock Spawn TS panels in either of these methods. Just ensure you are are doing an import and implements on the appropriate directive.

...
import { OnIniit } from '@angular/core';
...
import * as $      from 'jquery';  /* if using jQuery */
...
export class MainPageComponent implements ..., OnInit {
...

  ngOnInit(): void {

    ...

    /* ok time for some action! Use jQuery to fetch the hosting <divs> */

    const divDockContainer = $('#dock_div');  
    const divDockManager = $('#my_dock_manager');  

    /* configure options for Dock Spawn TS */

    const config: DockConfig = new DockConfig();  
    config.moveOnlyWithinDockConatiner = true;  

    /* 
     * create the Dock Spawn TS manager (and save reference for later) 
     *
     * Notice also that .get(0) is used to convert from a jQuery selector
     * result list to a DOM element, because Dock Spawn TS works with DOM
     * elements.
     *
     */

    this.dockManager = new DockManager(divDockManager.get(0), config);  
    this.dockManager.initialize();  

    /* 
     * Let the dock manager element fill in the entire component's space,
     * and keep on filling it even when it changes size.
     *   
     */  

    window.onresize = () => {  
      this.dockManager.resize(  
        divDockContainer.get(0).clientWidth,  
        divDockContainer.get(0).clientHeight  
      );  
    };      
    window.onresize(null);

    /*
     * to finish the setup, you will need to create panels and dock them,
     * just like the example on the Dock Spawn TS home page.  The steps 
     * for doing that will be very specific to your application.  As an 
     * example; maybe your application has a window manager and factory for 
     * generating windows and internally they have Dock Spawn TS panels. 
     * In that approach you would use your factory to build out your GUI.
     *  
     * Some examples are included below for working with dynamically created
     * panels and Angular components, it should be enough to get you going,
     * regardless of your overall architecture.
     *  
     */

    ... /* rest of your initialization */
  }
...

}

Creating Panels

The general steps for creating a panel; find the <div> being used to host it..create it, and dock it. For example:

/* 
 * which kind of panel is being created?  Here the enclosing class,
 *  has a field named 'tabbed', which flags which kind of panel it is.
 * 
 */

 const pType: PanelType = 
   this.tabbed ? PanelType.document : PanelType.panel;  

/*
 * create the panel and save its reference for later use.  Also, the
 * enclosing class already has a reference to the document manager (the 
 * field named '_manager'), and already has a panel title (the field 
 * named 'title'). 
 * 
 * For locating the <div> that will host the panel, jQuery is used to 
 * find it (the variable 'id' has the #<id> value), also note that 
 * .get(0) is used to convert from a jQuery selector result list...to 
 * an actual DOM element, because Dock Spawn TS works with DOM 
 * elements.
 *
 */

this._panel = 
  new PanelContainer(
    $(id).get(0), 
    this._manager, 
    this.title, 
    pType);
/*
 * now that we have a panel...we can dock it :)  In this example, 
 * the enclosing class already has as reference to the document
 * manager and we can usue that to get the node we are docking the 
 * panel in.  In this example we are givin the panel a fifth of the
 * IDE space on the left side.
 * 
 */

 let documentNode = this.dockManager.context.model.documentManagerNode;
 this.dockManager.dockLeft(documentNode, this.panel, 0.20);

Once you have created and docked your panel, Dock Spawn TS will move your content <div> to being a child inside the DOM parents that make up a panel. For example, from above, one of the panels was the #solution_window div. If $(id) above had been #solution_window, then after the creating and docking above, the DOM will have a new structure:

<div id="doc_div">
   ...
  <div class="panel-base" ...> /* outer wrapper for your panel */
    <div class="panel-titlebar ...> /* the header or tab */
    <div class="panel-content" ...>
      <div id="solution_window" ...>  /* your content hosting div */

Notice that your panel has been moved to be a child of the DOM elements that are all managed by Dock Spawn TS, to allow for resizing of panels, drag and drop etc.

If your hosting div (i.e. #solution_window) already had its content defined...then you are done! :)

Creating Panels with Dynamic Content

Once you have created and docked your panel, you may still have to define the content of the window. In some applications you can't define the content ahead of time; perhaps its loaded from a database or is configured by user preferences etc.

Creating your content dynamically is not so much more difficult, but you may need to be careful about timing; inserting new DOM elements doesn't necessarily mean they are ready right away. Your framework or your browser may delay when the DOM is actually ready.

To handle the potential timing issues, you can add dynamic content with these steps:

Some example code:

/* use jQuery to dynamically add a button */

$('#solution_window').
  html(`<div id='button_wrap'><button>Click Me</button></div>`

/* now wait for it to be ready, before we try to operate on it... */

const checkExist = setInterval(() => {  

  const dom: HTMLElement = 
    document.getElementById(`#button_wrap`) as HTMLElement;  

  if (dom) {  

    /* we're done polling, its ready */

    clearInterval(checkExist);  

    $(`#button_wrap button`).on('click', () => {

      alert(`Clicked!!`);
    });
  }
    /* wait 100ms before we check again */

}, 100);

For setting up your panel, other steps you might want to take:

If you are working in Angular there is still another step you'll need to take...dynamic creation of an angular component. An example of this is given below.

Dynamic Content with Angular Components

In the examples above, a panel was created, and docked, and dynamic content was even added and used. But still, this is not enough to work with arbitrary Angular components. With a little more code you can easily host any existing Angular component inside the panel you created.

You will not have to modify the existing angular component, but you will need to add some bridging code that will handle the steps of creating the component and also moving it to be a child of the DOM inside your panel. This last step of re-parenting makes sure the geometry of your component matches the panel...and when the dragging and dropping starts, the component goes wherever your panel goes :)

Lets say you have an existing angular component MyComponent, in a file my.component.ts (perhaps it also has a template file and other resources as well). You can't insert it directly into the Dock Spawn TS based panel...because your panel is not Angular, specifically its not an Angular View; in Angular everything is a hierarchy of views. So, when you used Dock Spawn TS...you went outside of the hierarchy, and now there is no where to insert (as a child) MyComponent.

This isn't actually a problem; DOM is DOM....you can happily have Angular based DOM and other DOM on the same web page. To insert MyComponent, the general steps are:

Once those steps are complete...your Angular component will act as a normal Angular component...but it will be visible inside a Dock Spawn TS panel (tabbed or regular).

There is a little plumbing to do as well; any component (and child components) that you are creating dynamically, must be declared as dynamic in your module settings, so in app.module.ts:

import { MyComponent }     from 'my.component';```
...

@NgModule({
declarations: [  
  AppComponent,
  ...
  MyComponent,
  ...
],
entryComponents: [  
  MyComponent,
],
...

Adding your component to entryComponents is the key step; it tells Angular to create a component factory for your component when your application is built. This is what allows you to create your component on the fly from anywhere in your application.

Next you need to add the bridging code that will actually create the component on the fly.

To continue the example above, inside the #solution_window panel, using the dynamic content creation method above, we will add a simple <div> to use as the anchor for the Angular component:

/* create a simple <div> to use as our anchor point */

$('#solution_window').
  html(`<div id='component_anchor'></div>`

/* now wait for it to be ready, before we try to create the component... */

const checkExist = setInterval(() => {  

  const dom: HTMLElement = 
    document.getElementById(`#component_anchor`) as HTMLElement;  

  if (dom) {  

    /* we're done polling, its ready */

    clearInterval(checkExist);  

    /* 
     * before we can do anything, we need a reference to the root 
     * view of page; or at least some Angular componenent.  Here, 
     * the enclosing class already has a field (named 'view') that
     * has a reference to the view.  That refernece was obtained 
     * from a component that wraps the whole page and is the root
     * componenent for the URL '/' or '/ide'...basically is the main
     * outer GUI component.  
     * 
     * On that one...we used constructor injection:
     *    
     *   constructor(private view: ViewContainerRef) {...}
     * 
     * to get a ViewContainerRef...that ref can now be used 
     * here as a surrogate for creating our dynamic component.
     * 
     * If you need to query the componenent you are creating 
     * from some parent component (say with @ViewChild)...use 
     * that component as the surrogate view.
     */

    const view: ViewContainerRef = this.view;  

    /* 
     * similarly, we picked some existing angular component to 
     * use constructor injection on to get a reference to the 
     * component factory in Angular.
     *  
     * If your application is such that you can't reach this 
     * code from an Angular component, just use a service as 
     * the bridge; pick a component to use for injecting your 
     * view and factory refs...pass them to a service, and then
     * access them from the service in this code.  Services can
     * be injected into any ordinary Type Script class as well 
     * as Angular component classes, so they are a natural bridge.
     *  
     */

    const factory: ComponentFactoryResolver = this.componentResolver;  

    /* ok, time to actually create the component ... */

    const creator      = factory.resolveComponentFactory(MyComponent);  
    const componentRef = view.createComponent(creator);  

    /* 
     * notice we used the component factory to create it, and it 
     * was created  in the surrogate view.  So its not going to be 
     * in the right location in the DOM.  But, it is there. :)
     * 
     * Finally capture a reference to the Angular component, but 
     * properly typed, so twe can naturally call its method and use
     * its public fields. 
     */ 

    this.myComponent  = componentRef.instance as StringViewerComponent;  

    /* 
     * final step, relocate it, so that its a child of our Dock Spawn
     * TS panel content...
     *  
     */

    const domElem = 
      (componentRef.hostView as EmbeddedViewRef<any>).rootNodes[0]
        as HTMLElement;  

    dom.appendChild(domElem);

    /* 
     * thats it! The Angular component is now visible and running 
     * insdie the Dock Spawn TS panel.
     *  
     */

  }

  /* wait 100ms before we check again */

}, 100);
dinobot71 commented 4 years ago

That covers the steps needed to basically create any angular component you like, inside a Dock Spawn TS panel. :)

jogibear9988 commented 4 years ago

I've added this to the wikki: https://github.com/node-projects/dock-spawn-ts/wiki/Angular-Integration

dinobot71 commented 4 years ago

looks good! I will try to find some hours at some point to add more typical Angular examples, with some bundled example code. I could see doing examples for some basic tasks such as changing title/icon, focusing on a tab, basic usage kind of stuff.

hartum commented 4 years ago

HI there, I am trying to use it with React, and after type this in console: yarn add dock-spawn-ts

The folder dock-spawn-ts is created inside node_modules (so far so good). Then goto to my editor and try to import modules in this way: import { DockManager } from 'dock-spawn-ts';

but i get an error: cannot find module 'dock-spawn-ts' or its corresponding type declarations.

any idea about how to solve this without have to write the full path to node_modules?

Myself answer: import { DockManager } from 'dock-spawn-ts/lib/js/DockManager'; import { PanelContainer } from 'dock-spawn-ts/lib/js/PanelContainer';

I leave it here in case it is useful for someone else

jogibear9988 commented 4 years ago

Try newest Version, I hopefully have fixed this atm