Star Rating Widget

Hi team,

May I know how could we simulate the rating widget system, let say 1 - 5 star with the existing webix component?

https://snippet.webix.com/jh9tsto9

@integral It is perfect…

I expanded on it to add editor support:

rating widget:

export function renderRating(value: number, readOnly: boolean = false, height: number = 20, id?: number, scale: number = 5) {
  id = id || webix.uid()

  let div = '<input type="hidden" id="' + id + '" value="' + value + '" />'

  if (!readOnly) {
    div += '<span class="webix_icon webix_rating_zero mdi mdi-close" data-value="0"></span>'
  }

  for (let i = 0; i < scale; i++) {
    div += '<span class="webix_icon webix_rating_point mdi mdi-star'

    if (value > i) {
      div += ' webix_rating_selected'
    }

    div += '" style="line-height:' + height + 'px;"'
    div += ' data-value="' + (i + 1) + '"></span>'
  }

  return div
}

webix.protoUI({
  name: 'rating',
  $cssName: 'text',
  $init: function () {
    this.on_click.webix_rating_point = function (e, id, target) {
      if (!this.config.readonly) {
        this.setValue(target.getAttribute('data-value'))
      }
    }

    this.on_click.webix_rating_zero = function (e, id, target) {
      if (!this.config.readonly) {
        this.setValue(target.getAttribute('data-value'))
      }
    }
  },

  $renderInput: function (config, div, id: number) {
    const value = parseInt(config.value || 0)

    div = renderRating(value, config.readonly as boolean, config.cheight as number, id)

    return webix.ui.text.prototype.$renderInput.apply(this, [config, div, id] as any)
  },

  $renderIcon: function () {
    return ''
  },

  $setValue: function () {
    this.refresh()
  },

  $getValue: function () {
    return this.config.value || 0
  }
}, webix.ui.text)

editor:

webix.editors.rating = {
  focus: function () { },

  onRatingClick(el: HTMLElement) {
    const val: number = parseInt(el.dataset.value as string)

    this.setValue(val)
  },

  getValue: function () {
    return this.getInputNode(this.node).value
  },

  setValue: function (value) {
    this.getInputNode(this.node).value = value

    this.node.innerHTML = renderRating(value)

    const stars = this.node.querySelectorAll('.webix_rating_point') as HTMLElement[]

    stars.forEach(el => {
      el.addEventListener('click', () => this.onRatingClick(el))
    })

    const zero = this.node.querySelector('.webix_rating_zero') as HTMLElement

    zero.addEventListener('click', () => this.onRatingClick(zero))
  },

  getInputNode: function () {
    return this.node.firstChild
  },

  render: function (value: number = 0): HTMLElement {
    return webix.html.create('div',
      {
        class: 'webix_dt_editor rating'
      },
      renderRating(value)
    )
  }
}

scss:

.webix_rating_zero {
  display: none;
  position: relative;
  top: 1px;
  color: $grey !important;
  cursor: pointer;

  &:hover {
    color: $font-color !important;
  }
}

.webix_dt_editor.rating {
  .webix_rating_zero {
    display: initial;
  }

  .webix_icon.webix_rating_point {
    cursor: pointer;
    color: $grey !important;

    &:hover {
      color: $gold !important;
    }

    &.webix_rating_selected {
      color: $gold !important;

      &:hover {
        color: darken($gold, 10%) !important;
      }
    }
  }
}

.webix_dt_editor.rating {
  padding-left: 12px;
}

.webix_icon.webix_rating_point {
  color: transparent !important;

  &.webix_rating_selected {
    color: $gold !important;
  }
}

column editor usage:

column.editor = 'rating'
column.template = renderRating(3) // Initial value