os-js / osjs-client

OS.js Client Module
https://manual.os-js.org/
Other
31 stars 31 forks source link

Auto Resizing of Dialog #130

Closed mahsashadi closed 3 years ago

mahsashadi commented 3 years ago

How dialog size could be changed according to its contents? I am using dynamic fields like Dynamic Field Example, inside my custom dialog.

andersevenrud commented 3 years ago

Try win.resizeFit(container) where container is the outer most container that overflows the window.

mahsashadi commented 3 years ago

I use resizeFit in my code as below, But it didn't work.

  createSettingDialog() {

    const callbackRender = ($content, dialogWindow, dialog) => {
      dialog.app = app({

        // state 

      }, {

        //actions 

      }, (state, actions) => {

        //view

        return dialog.createView([
          h('div', { 
            class: 'outerBox',
          }, [

            //inner components of view are here

        ])
        ]);
      }, $content);
    };

    // Values are passed down to the 'options' object
    const callbackValue = dialog => dialog.app.getValues();

    const callbackButton = (button, value) => {
      if (button === 'ok') {
        // some code
      }
    };

    const options = {
      buttons: ['ok', 'cancel'],
      window: {
        title: 'setting',
        message: 'message',
        //dimension: { width: 700, height: 800 },
        resizeFit:document.getElementsByClassName('outerBox')
      }
    };

    this.core.make('osjs/dialogs').create(options, callbackValue, callbackButton).render(callbackRender);
  }
andersevenrud commented 3 years ago

It's a method call, not a property.

mahsashadi commented 3 years ago

So where should I call this method? Two removeField and addField actions in guage.js file cause creating dynamic fields, which is returned to onWidgetTypeChange action in index.js file to add its hyperapp to dialog

gauge.js

showAdvancedSetting (grafana) {

    const state = {
      // state
    };

    const actions = {
      removeField:  (index) => {
          //some code
      },
      addField: (index)  => {
          //some code
      }
      //some other actions
    };

    const view = (state, actions) => (
      h(Box, {
        class: 'outer'
      },[

        // other view components inside outer box
    ])
    );

    return {state, actions, view};
  }

index.js

createSettingDialog() {

    const callbackRender = ($content, dialogWindow, dialog) => {
      dialog.app = app({

        // state 

      }, {

       //actions 

        onWidgetTypeChange: (widgetTypeValue) => {
          let div = document.getElementsByClassName('hidden-div');
          div[0].style.display = 'inline';
          advancedSetting = this.widget.showAdvancedSetting(this);
          app(advancedSetting.state, advancedSetting.actions, advancedSetting.view, div[0]);
        },

      }, (state, actions) => {

        //view

        return dialog.createView([
          h('div', { 
            class: 'outerBox',
          }, [
            //inner components of view are here
        ])
        ]);
      }, $content);
    };

    const callbackValue = dialog => dialog.app.getValues();

    const callbackButton = (button, value) => {
        // some code
    };

    const options = {
      //options
    };

    this.core.make('osjs/dialogs').create(options, callbackValue, callbackButton).render(callbackRender);
  }
andersevenrud commented 3 years ago

Try:

this.core
  .make('osjs/dialogs')
  .create(options, callbackValue, callbackButton)
  .on('render', win => win.resizeFit(win.$content.querySelector('.outerBox')))
  .render(callbackRender);
mahsashadi commented 3 years ago

It didn't work. It causes the following error:
Uncaught TypeError: this.core.make(...).create(...).on is not a function

andersevenrud commented 3 years ago

Ah. In the case of dialog instances it might have to be done like this:

    const callbackRender = ($content, dialogWindow, dialog) => {
      dialogWindow.on('render', win => win.resizeFit(win.$content.querySelector('.outerBox')))
      // ...
    }
andersevenrud commented 3 years ago

Come to think of it you can probably just do it like this:

    const callbackRender = ($content, dialogWindow, dialog) => {
      dialog.app = app({}, {}, (state, actions) => {}, $content);
      // Might need a setTimeout on this because rendering is defered
      dialogWindow.resizeFit($content.querySelector('.outerBox')) 
    };
mahsashadi commented 3 years ago

Unfortunately this doesn't fix the problem. The outerBox still overflows the dialog window.

andersevenrud commented 3 years ago

I had to take a look at this myself and found that in the case of these custom dialogs you have to override the CSS slightly:

    dialogWindow.on("render", () => {
      dialogWindow.$content.firstChild.style.bottom = "auto";
      dialogWindow.resizeFit();
    });

(without an argument in resizeFit, it will calculate with the first child of the inner contents, which is always fixed in a dialog -- hence the style change)

Full example here: https://codesandbox.io/s/osjs-custom-dialog-resize-fit-3ye2w?file=/src/package.js:1018-1157

mahsashadi commented 3 years ago

Thank you. In this way, resizing happens every time the dialog is rendered. But I need auto resizing while it is open, according to its dynamic content (as Dynamic Field Example).

andersevenrud commented 3 years ago

I've done some investegation into this, and the resizeFit probably won't fit your needs.

So I updated the codesandbox example to show how to do this dynamically. Sadly it involves some custom maths, but it's the only way you can do it at this moment.

Notice I removed the previous example here, so the CSS change is no longer required! I just added an event when you add a new entry to a list that does some calculations and figures out the correct size within the dialog.

I'm going to make the resizeFit a bit smarter to handle cases like this, but I need to think about it for a while.

mahsashadi commented 3 years ago

Thanks a lot. It works.

andersevenrud commented 3 years ago

Great!

I've opened an issue about improving this functionality so developers don't have to do all this calculation manually:

https://github.com/os-js/osjs-client/issues/131