jupyterlab / lumino

Lumino is a library for building interactive web applications
https://lumino.readthedocs.io/
Other
610 stars 128 forks source link

Switch from widget to standard web components #290

Open fcollonval opened 2 years ago

fcollonval commented 2 years ago

Problem

The current Widgets help us a lot at a time no clear frameworks were available to build advanced paneled UI. The situation is different now. So it would be good to move from on own thing to web-components. The advantages of using web components:

Proposed Solution

Switching from Widgets to web components sounds to hard as a breaking changes for downstream application. A better path seems to be a new components package that will progressively cover the components offer by the widgets package. And a wrapper to include the old widget in web-components could be deviced as demonstrated by @GordonSmith in GordonSmith/hpcc-js-wc.

Additional context

Some work on that front have started in GordonSmith/hpcc-js-wc. But the idea of this issue is to go a step further than wrapping widget inside web-components by making real web-components. I would suggest start of microsoft/fast to align on jupyterlab-contrib/jupyter-ui-toolkit and because it offers accessible web components and some useful components as menu, tabs and toolbar that will help extending to our own purpose.

GordonSmith commented 2 years ago

Some thoughts / suggestions:

  1. There shouldn't be a need to add a new dependency to the lumino project, I had initially used the microsoft/fast framework to create the web components, but the core spec is simple enough that its really not needed and I dropped it because:
    • I thought there was a nasty race condition (which in hindsight may be part of the spec when html components are "upgraded")
    • I wasn't happy with the observable broadcast mechanism they used (the one I now use is essentially my take on a lumino conflatable message)
  2. My motivation is the same as stated above, but personally I only see the "layout" widgets as being of interest and don't see any future in creating things like a "Lumino Pie Chart",
  3. From the start I have been asking myself if the base classes (or a variant of them) should be submitted to the lumino project (I would switch my "dispatch" to a lumino message for example), but wasn't going to suggest it until I had finished my POC and was ready to actually do a release.
  4. From luminos point of view, it should "just work" with web components (and by extension, just work with React/Angular/Vue components) its up to the juypter project to decide on "which" framework is appropriate to use (ms/fast, ms/fluent-ui etc.)
  5. Related to 3 above, but the lumino project needs to upgrade to a newer version of TypeScript (which I appreciate will be painful).
  6. Somewhat unrelated, but my "observable" demo is basically an extended markdown renderer which includes the observablehq.com runtime, I could see its integration into Juypter notebooks as quick win from a visualisation point of view?
fcollonval commented 2 years ago

Thanks a lot for sharing @GordonSmith

2. My motivation is the same as stated above, but personally I only see the "layout" widgets as being of interest and don't see any future in creating things like a "Lumino Pie Chart",

We may have some need for generic TreeView and Modal List components. But I agree that this will be more container components.

3. From the start I have been asking myself if the base classes (or a variant of them) should be submitted to the lumino project (I would switch my "dispatch" to a lumino message for example), but wasn't going to suggest it until I had finished my POC and was ready to actually do a release.

I opened this more for discussion than having an agenda (I won't have time to work on this in the near future). So I'm looking forward to see the final version of your POC :wink:

4. From luminos point of view, it should "just work" with web components (and by extension, just work with React/Angular/Vue components) its up to the juypter project to decide on "which" framework is appropriate to use (ms/fast, ms/fluent-ui etc.)

:+1:

5. Related to 3 above, but the lumino project needs to upgrade to a newer version of TypeScript (which I appreciate will be painful).

This is also a goal for v2.

6. Somewhat unrelated, but my "observable" demo is basically an extended markdown renderer which includes the  observablehq.com runtime, I could see its integration into Juypter notebooks as quick win from a visualisation point of view?

The latest trend on that subject is to move to markdown-it. But nobody is working on it currently. Anyway this could still be a nice extension for Jupyter; thanks for the suggestion.

GordonSmith commented 2 years ago

FYI I finally got around to releasing the first version: http://hpcc-systems.github.io/Visualization/components/

To install: npm install --save @hpcc-js/wc-layout

Note: It includes React intrinsic typings for folks using TypeScript and React.

Hello World:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8" />
    <title>Test Layouts</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@hpcc-js/common/font-awesome/css/font-awesome.min.css">
    <script src="https://cdn.jsdelivr.net/npm/@hpcc-js/wc-layout"></script>
</head>

<body>

    <h1>Splitters</h1>
    <hpcc-splitpanel orientation="horizontal" style="width:100%;height:400px">
        <div style="overflow:auto;min-width:48px">
            <h1>HTML Ipsum Presents</h1>
            <p><strong>Pellentesque habitant morbi tristique</strong> senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. <em>Aenean ultricies mi vitae est.</em> Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, <code>commodo vitae</code>, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. <a href="#">Donec non enim</a> in turpis pulvinar facilisis. Ut felis.</p>
        </div>
        <hpcc-splitpanel orientation="vertical" style="width:100%;height:100%;border:0px;padding:0px;min-width:48px">
            <div style="overflow:auto;min-height:48px">
                <h1>HTML Ipsum Presents</h1>
                <p><strong>Pellentesque habitant morbi tristique</strong> senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante.</p>
            </div>
            <div style="overflow:auto;min-height:48px">
                <h1>HTML Ipsum Presents</h1>
                <p><strong>Pellentesque habitant morbi tristique</strong> senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante.</p>
            </div>
        </hpcc-splitpanel>
    </hpcc-splitpanel>

    <h1>Tabs</h1>
    <hpcc-tabpanel style="width:100%;height:400px">
        <div data-label="AAA" style="overflow:auto;min-width:48px">
            <h1>AAA Ipsum Presents</h1>
            <p><strong>Pellentesque habitant morbi tristique</strong> senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. <em>Aenean ultricies mi vitae est.</em> Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, <code>commodo vitae</code>, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. <a href="#">Donec non enim</a> in turpis pulvinar facilisis. Ut felis.</p>
        </div>
        <div data-label="BBB" style="overflow:auto;min-width:48px">
            <h1>BBB Ipsum Presents</h1>
            <p><strong>Pellentesque habitant morbi tristique</strong> senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. <em>Aenean ultricies mi vitae est.</em> Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, <code>commodo vitae</code>, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. <a href="#">Donec non enim</a> in turpis pulvinar facilisis. Ut felis.</p>
        </div>
    </hpcc-tabpanel>

    <h1>Dock Panel</h1>
    <hpcc-dockpanel style="width:100%;height:400px">
        <div id="one" data-label="AAAA" style="overflow:auto;min-width:48px;min-height:48px">
            <h1>AAAA HTML Ipsum Presents</h1>
            <p><strong>Pellentesque habitant morbi tristique</strong> senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. <em>Aenean ultricies mi vitae est.</em> Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, <code>commodo vitae</code>, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. <a href="#">Donec non enim</a> in turpis pulvinar facilisis. Ut felis.</p>
        </div>
        <div id="three" data-mode="split-right" data-closable="true" style="overflow:auto;min-width:48px;min-height:48px">
            <h1>CCCC HTML Ipsum Presents</h1>
            <p><strong>Pellentesque habitant morbi tristique</strong> senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. <em>Aenean ultricies mi vitae est.</em> Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, <code>commodo vitae</code>, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. <a href="#">Donec non enim</a> in turpis pulvinar facilisis. Ut felis.</p>
        </div>
        <div data-mode="tab-after" data-ref="three" data-caption="What no label!" style="overflow:auto;min-width:48px;min-height:48px">
            <h1>DDDD HTML Ipsum Presents</h1>
            <p><strong>Pellentesque habitant morbi tristique</strong> senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. <em>Aenean ultricies mi vitae est.</em> Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, <code>commodo vitae</code>, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. <a href="#">Donec non enim</a> in turpis pulvinar facilisis. Ut felis.</p>
        </div>
        <div data-mode="split-bottom" data-ref="one" style="overflow:auto;min-width:48px;min-height:48px">
            <h1>BBBB HTML Ipsum Presents</h1>
            <p><strong>Pellentesque habitant morbi tristique</strong> senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. <em>Aenean ultricies mi vitae est.</em> Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, <code>commodo vitae</code>, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. <a href="#">Donec non enim</a> in turpis pulvinar facilisis. Ut felis.</p>
        </div>
    </hpcc-dockpanel>
    <script>
        document.querySelector("hpcc-dockpanel").addEventListener("closeRequest", function (evt) {
            if (!confirm(`Close Tab "${evt.detail.tagName} #${evt.detail.id}"?`)) {
                evt.preventDefault();
            }
        });
    </script>
</body>

</html>
blink1073 commented 2 years ago

Very nice, the demo site is beautiful as well!

fcollonval commented 2 years ago

Thanks a lot for sharing @GordonSmith And indeed the demo site is awesome.

I got a question regarding React. I saw you are using preact that support web-components. Did you try with the experimental React v18 too? If yes, did you encountered some troubles or it worked out of the box?

GordonSmith commented 2 years ago

For the web components I am not using preact or any other 3rd party libraries outside of the ones I am wrapping in specific components, like Lumino.

For React and JavaScript they should "just work".

For the React and TypeScript I add some meta information to the global JSX.IntrinsicElements object, which TypeScript uses to syntax check the attributes and properties - looks like this:

declare global {
    namespace JSX {
        interface IntrinsicElements {
            ["hpcc-dockpanel"]: WebComponent<HPCCDockPanelElement, "layoutChanged">;
        }
    }
}

Again I expect it to just work on all versions of React (but have not tested with v18). I have only done preliminary testing with v16 but should start using it anger in the next couple of weeks (a React / FluentUI application).

GordonSmith commented 2 years ago

BTW thanks for the comments on the web site, it is currently out of sync with the sources so will be getting some updates over the comming days / weeks.

The really nice thing about this setup is that I can use the documentation / demo site as a development target for hot code updates and VSCode debugging etc. for both markdown and typescript!

fcollonval commented 2 years ago

For React and JavaScript they should "just work".

FYI the standard React library does not fully support custom elements. In particular passing React props to custom element properties or event listeners is known to have issues.

Some resources:

GordonSmith commented 2 years ago

Thanks for those links and I will report back once I start some real world testing. But for now:

Handling data
React passes all data to Custom Elements in the form of HTML attributes. For primitive data this is fine, but the system breaks down when passing rich data, like objects or arrays. In these instances you end up with stringified values like some-attr="[object Object]" which can't actually be used.

In my docs I distinguish between "Attributes" and "Properties", where properties expect "complex" data structures and should be set via ref.prop = XXX (this is primarily for performance, to avoid serialization / deserialization overheads). (Note: The documentation is broken currently and is displaying the attributes twice and not the properties)

WRT to the event handling I will need to test further - I am adding meta information re the events to the global intrinsic instance (which I think is what React does for the native html elements) so am hopeful it won't need a wrapper - time will tell.