1

I'm writing a custom UI for a matlab App.

I have perfectly working code in HTML, CSS, Javascript which i tested in my web browser (Edge). I import this code using the standard library HTML component available for Matlab app designer, but the rendering is not quite correct. The functionality is perfectly retained but there are some missing icons.

The missing icons are linked from google material icons library and i'm pretty sure that this referencing is the issue. Also the font is not retained but it is a minor problem.

This is what i have in Matlab app:

matlabapp

and this is what i have in the browser:

edge

as you can see both 'delete' entry icons and 'open drop down menu' icon are not rendered.

This is the code i wrote:

HTML:

(line 7 is where the issue lies in my opinion since the app is not really web based the href does nothing)

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

<head>
  <meta charset="UTF-8">
  <title>MultiSelect </title>
  
  
<link rel='stylesheet' href='https://fonts.googleapis.com/css?family=Raleway'>
<link rel='stylesheet' href='https://fonts.googleapis.com/icon?family=Material+Icons'> 

<link rel="stylesheet" href="css/style-copy.css">

</head>
<body>

  <section>

    <select multiple="multiple" id="myMulti">
      <option>Item 1</option>
      <option>item 2</option>
      <option>item 3</option>
      <option>item 4</option>
      
    </select>
  </section> 
<script  src="js/index.js"></script>

</body>
</html>

CSS:

@charset "UTF-8";


html, body {
  padding: 0px;
  margin: 0px;
  background: #222;
  font-family: 'Raleway', sans-serif;
  color: #444;
}

body * {
  box-sizing: border-box;
  margin: 0px;
  padding: 0px;
  color: #222;
}
body section {
  margin-bottom: 10px;
}

.container {
  max-width: 600px;
  margin: 40px auto;
  background: #cdcdcd;
  min-height: 330px;
  height: 400px;
  padding: 20px 20px;
}

.drop {
  position: relative;
  -webkit-user-select: none;
     -moz-user-select: none;
      -ms-user-select: none;
          user-select: none;
}
.drop.open {
  z-index: 100;
}
.drop.open .drop-screen {
  z-index: 100;
  display: block;
}
.drop.open .drop-options {
  z-index: 200;
  max-height: 200px;
}
.drop.open .drop-display {
  z-index: 200;
  border-color: #465;
}
.drop select {
  display: none;
}
.drop .drop-screen {
  position: fixed;
  width: 100%;
  height: 100%;
  background: #000;
  top: 0px;
  left: 0px;
  opacity: 0;
  display: none;
  z-index: 1;
}

.link {
  text-align: center;
  margin: 20px 0px;
  color:#8CACD7;
}


.drop .drop-display {
  position: relative;
  padding: 0px 20px 5px 5px;
  border: 4px solid #444;
  width: 100%;
  background: #FFF;
  z-index: 1;
  margin: 0px;
  font-size: 16px;
  min-height: 58px;
}
.drop .drop-display:hover:after {
  opacity: 0.75;
}
.drop .drop-display:after {
  font-family: 'Material Icons';
  content: "\e5c6";
  position: absolute;
  right: 10px;
  top: 12px;
  font-size: 24px;
  color: #444;
}
.drop .drop-display .item {
  position: relative;
  display: inline-block;
  border: 2px solid #333;
  margin: 5px 5px -4px 0px;
  padding: 0px 25px 0px 10px;
  overflow: hidden;
  height: 40px;
  line-height: 36px;
}
.drop .drop-display .item .btnclose {
  color: #444;
  position: absolute;
  font-size: 16px;
  right: 5px;
  top: 10px;
  cursor: pointer;
}
.drop .drop-display .item .btnclose:hover {
  opacity: 0.75;
}
.drop .drop-display .item.remove {
  -webkit-animation: removeSelected 0.2s, hide 1s infinite;
          animation: removeSelected 0.2s, hide 1s infinite;
  -webkit-animation-delay: 0s, 0.2s;
          animation-delay: 0s, 0.2s;
}
.drop .drop-display .item.add {
  -webkit-animation: addSelected 0.2s;
          animation: addSelected 0.2s;
}
.drop .drop-display .item.hide {
  display: none;
}
.drop .drop-options {
  background: #444;
  box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.25);
  position: absolute;
  width: 100%;
  max-height: 0px;
  overflow-y: auto;
  transition: all 0.25s linear;
  z-index: 1;
}
.drop .drop-options a {
  display: block;
  height: 40px;
  line-height: 40px;
  padding: 0px 20px;
  color: white;
  position: relative;
  max-height: 40px;
  transition: all 1s;
  overflow: hidden;
}
.drop .drop-options a:hover {
  background: #465;
  cursor: pointer;
}
.drop .drop-options a.remove {
  -webkit-animation: removeOption 0.2s;
          animation: removeOption 0.2s;
  max-height: 0px;
}
.drop .drop-options a.add {
  -webkit-animation: addOption 0.2s;
          animation: addOption 0.2s;
}
.drop .drop-options a.hide {
  display: none;
}

@-webkit-keyframes pop {
  from {
    -webkit-transform: scale(0);
            transform: scale(0);
  }
  to {
    -webkit-transform: scale(1);
            transform: scale(1);
  }
}

@keyframes pop {
  from {
    -webkit-transform: scale(0);
            transform: scale(0);
  }
  to {
    -webkit-transform: scale(1);
            transform: scale(1);
  }
}
@-webkit-keyframes removeOption {
  from {
    max-height: 40px;
  }
  to {
    max-height: 0px;
  }
}
@keyframes removeOption {
  from {
    max-height: 40px;
  }
  to {
    max-height: 0px;
  }
}
@-webkit-keyframes addOption {
  from {
    max-height: 0px;
  }
  to {
    max-height: 40px;
  }
}
@keyframes addOption {
  from {
    max-height: 0px;
  }
  to {
    max-height: 40px;
  }
}
@-webkit-keyframes removeSelected {
  from {
    -webkit-transform: scale(1);
            transform: scale(1);
  }
  to {
    -webkit-transform: scale(0);
            transform: scale(0);
  }
}
@keyframes removeSelected {
  from {
    -webkit-transform: scale(1);
            transform: scale(1);
  }
  to {
    -webkit-transform: scale(0);
            transform: scale(0);
  }
}
@-webkit-keyframes addSelected {
  from {
    -webkit-transform: scale(0);
            transform: scale(0);
  }
  to {
    -webkit-transform: scale(1);
            transform: scale(1);
  }
}
@keyframes addSelected {
  from {
    -webkit-transform: scale(0);
            transform: scale(0);
  }
  to {
    -webkit-transform: scale(1);
            transform: scale(1);
  }
}
@-webkit-keyframes hide {
  from, to {
    max-height: 0px;
    max-width: 0px;
    padding: 0px;
    margin: 0px;
    border-width: 0px;
  }
}
@keyframes hide {
  from, to {
    max-height: 0px;
    max-width: 0px;
    padding: 0px;
    margin: 0px;
    border-width: 0px;
  }
}

JS:

var $ = {
   get: function(selector){ 
      var ele = document.querySelectorAll(selector);
      for(var i = 0; i < ele.length; i++){
         this.init(ele[i]);
      }
      return ele;
   },
   template: function(html){
      var template = document.createElement('div');
      template.innerHTML = html.trim();
      return this.init(template.childNodes[0]);
   },
   init: function(ele){
      ele.on = function(event, func){ this.addEventListener(event, func); }
      return ele;
   }
};

//Build the plugin
var drop = function(info){var o = {
   options: info.options,
   selected: info.selected || [],
   preselected: info.preselected || [],
   open: false,
   html: {
      select: $.get(info.selector)[0],
      options: $.get(info.selector + ' option'),
      parent: undefined,
   },
   init: function(){
      //Setup Drop HTML
      this.html.parent = $.get(info.selector)[0].parentNode
      this.html.drop = $.template('<div class="drop"></div>')
      this.html.dropDisplay = $.template('<div class="drop-display">Display</div>')
      this.html.dropOptions = $.template('<div class="drop-options">Options</div>')
      this.html.dropScreen = $.template('<div class="drop-screen"></div>')
      
      this.html.parent.insertBefore(this.html.drop, this.html.select)
      this.html.drop.appendChild(this.html.dropDisplay)
      this.html.drop.appendChild(this.html.dropOptions)
      this.html.drop.appendChild(this.html.dropScreen)
      //Hide old select
      this.html.drop.appendChild(this.html.select);
      
      //Core Events
      var that = this;
      this.html.dropDisplay.on('click', function(){ that.toggle() });
      this.html.dropScreen.on('click', function(){ that.toggle() });
      //Run Render
      this.load()
      this.preselect()
      this.render();
   },
   toggle: function(){
      this.html.drop.classList.toggle('open');
   },
   addOption: function(e, element){ 
      var index = Number(element.dataset.index);
      this.clearStates()
      this.selected.push({
         index: Number(index),
         state: 'add',
         removed: false
      })
      this.options[index].state = 'remove';
      this.render()
   },
   removeOption: function(e, element){
      e.stopPropagation();
      this.clearStates()
      var index = Number(element.dataset.index);
      this.selected.forEach(function(select){
         if(select.index == index && !select.removed){
            select.removed = true
            select.state = 'remove'
         }
      })
      this.options[index].state = 'add'
      this.render();
   },
   load: function(){
      this.options = [];
      for(var i = 0; i < this.html.options.length; i++){
         var option = this.html.options[i]
         this.options[i] = {
            html:  option.innerHTML,
            value: option.value,
            selected: option.selected,
            state: ''
         }
      }
   },
   preselect: function(){
      var that = this;
      this.selected = [];
      this.preselected.forEach(function(pre){
         that.selected.push({
            index: pre,
            state: 'add',
            removed: false
         })
         that.options[pre].state = 'remove';
      })
   },
   render: function(){
      this.renderDrop()
      this.renderOptions()
   },
   renderDrop: function(){ 
      var that = this;
      var parentHTML = $.template('<div></div>')
      this.selected.forEach(function(select, index){ 
         var option = that.options[select.index];
         var childHTML = $.template('<span class="item '+ select.state +'">'+ option.html +'</span>')
         var childCloseHTML = $.template(
            '<i class="material-icons btnclose" data-index="'+select.index+'">&#xe5c9;</i></span>')
         childCloseHTML.on('click', function(e){ that.removeOption(e, this) })
         childHTML.appendChild(childCloseHTML)
         parentHTML.appendChild(childHTML)
      })
      this.html.dropDisplay.innerHTML = ''; 
      this.html.dropDisplay.appendChild(parentHTML)
   },
   renderOptions: function(){  
      var that = this;
      var parentHTML = $.template('<div></div>')
      this.options.forEach(function(option, index){
         var childHTML = $.template(
            '<a data-index="'+index+'" class="'+option.state+'">'+ option.html +'</a>')
         childHTML.on('click', function(e){ that.addOption(e, this) })
         parentHTML.appendChild(childHTML)
      })
      this.html.dropOptions.innerHTML = '';
      this.html.dropOptions.appendChild(parentHTML)
   },
   clearStates: function(){
      var that = this;
      this.selected.forEach(function(select, index){ 
         select.state = that.changeState(select.state)
      })
      this.options.forEach(function(option){ 
         option.state = that.changeState(option.state)
      })
   },
   changeState: function(state){
      switch(state){
         case 'remove':
            return 'hide'
         case 'hide':
            return 'hide'
         default:
            return ''
       }
   },
   isSelected: function(index){
      var check = false
      this.selected.forEach(function(select){ 
         if(select.index == index && select.removed == false) check = true
      })
      return check
   }
}; o.init(); return o;}


//Set up some data
var options = [
   { html: 'cats', value: 'cats' },
   { html: 'fish', value: 'fish' },
   { html: 'squids', value: 'squids' },
   { html: 'cats', value: 'whales' },
   { html: 'cats', value: 'bikes' },
];

var myDrop = new drop({
   selector:  '#myMulti',
   preselected: [0, 2]
});
 myDrop.toggle();

I'm wondering if there is an issue in the code or if the functionality is not available at all. I found nothing on mathworks forum and documentation.

Ferro Luca
  • 459
  • 11
  • 1
    Please create a [**Minimal** reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) – ATP Jul 28 '23 at 12:52
  • 1
    it is reproducible, you just have to create a blank app and, as i wrote, drag and drop the HTML standard component from the library and link the .html file as source. If you dont have matlab it wont be reproducible clearly – Ferro Luca Jul 28 '23 at 13:12
  • 1
    In the `uihtml` docs at the ["Limitations" paragraph](https://www.mathworks.com/help/matlab/ref/uihtml.html#mw_d8fc1e12-fa02-43b8-bfaa-db9dd0f2c8dd) it says that it can embed *locally saved* resources, with support certain for js and css only, but other types might (or might not) be supported. So it's worth trying a local copy with woff and tff files. And if it's not working, you may use [svg](https://developers.google.com/fonts/docs/material_icons#svg) versions of the icons you use, and those you can embed in your html file if need be. – kikon Jul 30 '23 at 05:26
  • I was thinking about the same solution as well, but first i wanted to be sure that this approach is not feasible. Maybe i was missing something relevant to make it work – Ferro Luca Jul 31 '23 at 12:04

0 Answers0