Rating Widget

I read an article comparing React vs Angular. I implemented the MVC design pattern in Javascript for this widget to show that there's lots of frameworks out there.

by Joey Guerra

HTML

<section>
  <h1>Ratings</h1>
  <h2>Clickable Rating<span id='clickable-rating'></span></h2>
  <div id='fundooRating'>
    <ul class='rating'>
      <li>★</li>
      <li>★</li>
      <li>★</li>
      <li>★</li>
      <li>★</li>
      <li>★</li>
      <li>★</li>
      <li>★</li>
      <li>★</li>
      <li>★</li>
    </ul>
  </div>
  <h2>Readonly rating</h2>
  <div id='readonlyRating'>
    <ul class='rating'>
      <li>★</li>
      <li>★</li>
      <li>★</li>
      <li>★</li>
      <li>★</li>
      <li>★</li>
      <li>★</li>
      <li>★</li>
      <li>★</li>
      <li>★</li>
    </ul>
  </div>
</section>

CSS

.rating{
    color: #a9a9a9;
    margin: 0;
    padding: 0;
  }

  ul.rating {
    display: inline-block;
  }

  .rating li {
    list-style-type: none;
    display: inline-block;
    padding: 1px;
    text-align: center;
    font-weight: bold;
    cursor: pointer;
  }

  .rating .selected {
    color: #21568b;
  }

JavaScript

const makeKeyValueObservable = observable => {
    let cached = Object.assign({}, observable)
    let proxied = {}
    let observers = {}
    const everyKeyObservers = []
    const changed = (key, old, value) => {
      if(everyKeyObservers.length > 0) {
        everyKeyObservers.forEach(o=>{
            if(o.update) o.update(key, old, value)
            else o(key, old, value)
        })
      }
      if(!observers[key]) return
      observers[key]?.forEach(o=>{
        if(o.update) o.update(key, old, value)
        else o(key, old, value)
      })
    }
    const api = {
        observe(key, observer){
          if(typeof key == 'function') {
              everyKeyObservers.push(key)
              return
          }
          if(!observers[key]) observers[key] = []
          observers[key].push(observer)
        },
        stopObserving(key, observer){
          if(typeof key == 'function') {
              const i = everyKeyObservers.findIndex(o => o === key)
              if(i == -1) return
              everyKeyObservers.splice(i, 1)
              return
          }
          if(!observers[key]) return
          const i = observers[key].findIndex(o => o === observer)
          if(i == -1) return
          observers[key].splice(i, 1)
        },
        release(){
          console.log('observable releasing')
          everyKeyObservers.length = 0
          Object.keys(observers).forEach(key => {
              observers[key].length = 0
          })
        }
    }
    
    const makeProxy = obj => {
      let p = new Proxy(obj, {
        get(target, prop, receiver){
          return obj[prop]
        },
        set(target, prop, value){
          if(undefined == target[prop] && Array.isArray(value)) {
            value = new ObservableArray(...value)
          } else if(undefined == target[prop] && typeof value == 'object') {
            value = makeKeyValueObservable(value)
          }
          const old = obj[prop]
          obj[prop] = value
         ...