ThatOpen / web-ifc-viewer

Graphics engine and toolkit for client applications.
MIT License
954 stars 235 forks source link

IfcViewerAPI breaks some functionality in Bootstrap5 #163

Closed vulevukusej closed 1 year ago

vulevukusej commented 2 years ago

It seems that there is something in IfcViewerAPI that breaks some of the functionality in BS5, such as dropdowns, tabs and so on.

If I remove bundle.js, which is just the file produced by rollup when I use import { IfcViewerAPI } from "web-ifc-viewer"; then the elements work as expected.

<!DOCTYPE html>
<html lang="en">
<head>
  <title>Bootstrap Example</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/css/bootstrap.min.css" rel="stylesheet">
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/js/bootstrap.bundle.min.js"></script>
  <script src="bundle.js"></script>
</head>
<body>

<div class="container mt-3">
  <h2>Dropdowns</h2>
  <p>The .dropdown-divider class is used to separate links inside the dropdown menu with a thin horizontal line:</p>

  <div class="dropdown">
    <button type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown">
      Dropdown button
    </button>
    <ul class="dropdown-menu">
      <li><a class="dropdown-item" href="#">Link 1</a></li>
      <li><a class="dropdown-item" href="#">Link 2</a></li>
      <li><a class="dropdown-item" href="#">Link 3</a></li>
      <li><hr class="dropdown-divider"></li>
      <li><a class="dropdown-item" href="#">Another link</a></li>
    </ul>
  </div>
</div>

</body>
</html>
agviegas commented 2 years ago

Hi! I'm guessing that this happens because the div that contains the viewer is supposed to be empty. If you want to create menus, you should do it in a completely separate HTML element.

vulevukusej commented 2 years ago

It doesn't matter where I place the menu, it seems as if something in the code removes the event listener from the bootstrap elements. I'll experiment some more and report back if I find a workaround.

vulevukusej commented 2 years ago

Any updates on this? No matter what I try, it seems something within ifcjs conflicts with the javascript-dependent elements in bootstrap.

agviegas commented 2 years ago

Hey @vulevukusej are you sure that the div that you pass to web-ifc-viewer is empty (0 HTML elements inside)?

vulevukusej commented 2 years ago

@agviegas In the example above there isn't even a div defined for the viewer, and I never even try to load a model. Just importing the IfcViewerAPI is enough to break some javascript functionality in bootstrap. For reference, the exact same code works flawlessly if I just use three.js or xeokit.

agviegas commented 2 years ago

@vulevukusej in the example above, what is the HTML element that you are using to render the scene?

vulevukusej commented 2 years ago

@agviegas sorry if i'm not explaining it clearly. I intentionally skip rendering the scene or loading any model. I wanted to figure out myself what is causing the conflict. My javascript file includes the following 2 lines only:

import { Color } from 'three';
import { IfcViewerAPI } from 'web-ifc-viewer';

and the html file has nothing else other than basic dropdowns with bootstrap classes.

agviegas commented 2 years ago

Just to be sure we are on the same page, you are generating the bundle.js file yourself, right? Can you please update a reproducible example?

vulevukusej commented 2 years ago

Just to be sure we are on the same page, you are generating the bundle.js file yourself, right? Can you please update a reproducible example?

Yes I'm bundling myself. On my way home so I'll upload an example to my repo tomorrow. Cheers.

vulevukusej commented 2 years ago

@agviegas Good Morning! - Here's the repo with small example demonstrating the issue: https://github.com/vulevukusej/Bootstrap-IFCjs

With this line of code commented out, the bootstrap functionality works as expected; clicking on the button opens the offcanvas sidebar.

aka-blackboots commented 2 years ago

::take

agviegas commented 2 years ago

🟢🟢🟢

::take

Hi, @aka-blackboots! Thanks for taking this bounty! The due date is December 22, 2022 UTC.

If you need to submit some pull requests (PR) to complete the tasks, make sure that the last and only the last PR has a title that either starts with the bounty ID or is exactly the same as the bounty name. After the PR is merged, this bounty’s status will automatically changed to done.

If you do not need to make a PR, tell the manager @agviegas to run ::done command after your tasks is confirmed to be done.

Good luck!

aka-blackboots commented 2 years ago

::drop

agviegas commented 2 years ago

🟢🟢🟢

::drop

Hi, @aka-blackboots! Thanks for giving it a try! It’s now once again available for anyone to take.

vulevukusej commented 2 years ago

To anyone else who might've also encountered this - a temporary workaround is to simply initiate the bootstrap elements manually in javascript.: newCanvasElement = new bootstrap.Offcanvas(_HtmlElement_)

agviegas commented 1 year ago

Hey @vulevukusej I looked at this and, to be honest, have no idea about what's happening. If that last comment solves the issue, that's going to be the solution!

aozien commented 1 year ago

::take

agviegas commented 1 year ago

🟢🟢🟢

::take

Hi, @aozien! Thanks for taking this bounty! The due date is January 8, 2023 UTC.

If you need to submit some pull requests (PR) to complete the tasks, make sure that the last and only the last PR has a title that either starts with the bounty ID or is exactly the same as the bounty name. After the PR is merged, this bounty’s status will automatically changed to done.

If you do not need to make a PR, tell the manager @agviegas to run ::done command after your tasks is confirmed to be done.

Good luck!

aozien commented 1 year ago

Hey everyone, I found out what's causing this conflict,

tl;dr (The solution):

Node.ELEMENT_NODE = 1; //that line will fix it

The reason this conflict happens:

Bootstrap relies on the global object property Window.Node.ELEMENT_NODE to determine whether or not it should raise the event, like this

const isDisabled = element => { if (!element || element.nodeType !== Node.ELEMENT_NODE) return true; if (element.classList.contains('disabled')) return true; if (typeof element.disabled !== 'undefined') return element.disabled; return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false'; };

It makes sure that element.nodeType is of type Node.ELEMENT_NODE,

However, one of web-ifc dependencies (earcut) overrides the global object Node, and changes it to a function constructor for this struct Node

which leads to Node.ELEMENT_NODE being undefined instead of its actual value of '1', so the line element.nodeType !== Node.ELEMENT_NODE will always make isDisabled = true ,and that's why all of the bootstrap element events will not trigger.

Conclusion

Unfortunately, since this is caused by the wasm module itself, and by a third party dependency, we'd need to either change the bootstrap js file or the hpp file to avoid this conflict.

However, we can do the second best thing, which is simply redefining the Node.ELEMENT_NODE to be equal to '1',

Node.ELEMENT_NODE = 1; //that should fix your problem

You can execute this line at any time after loading the wasm files and it'll work again.

Hope that helps!

agviegas commented 1 year ago

::done

agviegas commented 1 year ago

🟢🟢🟢

::done

Hi, @aozien! Thanks for your contributions! Please submit an expense to IFC.js Open Collective. Then, tell us the invoice number via the ::expense::_____ command (replace the _____ with the invoice number).

vulevukusej commented 1 year ago

@aozien thanks, you're awesome ☺️

aozien commented 1 year ago

@aozien thanks, you're awesome ☺️

No problem, you did a great job too 😄

aozien commented 1 year ago

::expense::114333

agviegas commented 1 year ago

🟢🟢🟢

::expense::114333

Hi, @aozien! Thanks for the confirmation! We’ll proceed to review the expense. Once it’s approved, the payment will be scheduled.