<div class="star-rating" data-item-id="XXXX" data-endpoint="/api/rating-useful.json" data-average-rating="2.4" data-rating-type="useful">
    <form class="star-rating__form" action="">
        <fieldset class="star-rating__fieldset">
            <legend class="star-rating__legend label">Nützliches Thema?</legend>
            <div class="star-rating__inner">
                <div class="star-rating__stars">
                    <input class="star-rating__input" type="radio" id="XXXX-1" name="rating" value="1" />
                    <label class="star-rating__label" for="XXXX-1" title="ein Stern"><span class="u-hidden-visually">1 Stern</span></label>
                    <input class="star-rating__input" type="radio" id="XXXX-2" name="rating" value="2" />
                    <label class="star-rating__label" for="XXXX-2" title="zwei Sterne"><span class="u-hidden-visually">2 Sterne</span></label>
                    <input class="star-rating__input" type="radio" id="XXXX-3" name="rating" value="3" />
                    <label class="star-rating__label" for="XXXX-3" title="drei Sterne"><span class="u-hidden-visually">3 Sterne</span></label>
                    <input class="star-rating__input" type="radio" id="XXXX-4" name="rating" value="4" />
                    <label class="star-rating__label" for="XXXX-4" title="vier Sterne"><span class="u-hidden-visually">4 Sterne</span></label>
                    <input class="star-rating__input" type="radio" id="XXXX-5" name="rating" value="5" />
                    <label class="star-rating__label" for="XXXX-5" title="fünf Sterne"><span class="u-hidden-visually">5 Sterne</span></label>
                </div><span class="star-rating__count"><span class="u-hidden-visually">Abgegebene Bewertungen</span><span>(12)</span></span>
            </div>
            <input class="star-rating__submit" type="submit" />
        </fieldset>
    </form>
</div>
.star-rating(data-item-id=id, data-endpoint=endpoint, data-average-rating=avgRating, data-rating-type=ratingType)
  form.star-rating__form(action='')
    fieldset.star-rating__fieldset
      legend.star-rating__legend.label #{title}

      .star-rating__inner
        .star-rating__stars
          input.star-rating__input(type='radio', id=`${id}-1`, name='rating', value='1')
          label.star-rating__label(for=`${id}-1`, title='ein Stern')
            span.u-hidden-visually 1 Stern

          input.star-rating__input(type='radio', id=`${id}-2`, name='rating', value='2')
          label.star-rating__label(for=`${id}-2`, title='zwei Sterne')
            span.u-hidden-visually 2 Sterne

          input.star-rating__input(type='radio', id=`${id}-3`, name='rating', value='3')
          label.star-rating__label(for=`${id}-3`, title='drei Sterne')
            span.u-hidden-visually 3 Sterne

          input.star-rating__input(type='radio', id=`${id}-4`, name='rating', value='4')
          label.star-rating__label(for=`${id}-4`, title='vier Sterne')
            span.u-hidden-visually 4 Sterne

          input.star-rating__input(type='radio', id=`${id}-5`, name='rating', value='5')
          label.star-rating__label(for=`${id}-5`, title='fünf Sterne')
            span.u-hidden-visually 5 Sterne

        span.star-rating__count
          span.u-hidden-visually Abgegebene Bewertungen
          span (#{count})

      input.star-rating__submit(type='submit')
{
  "id": "XXXX",
  "title": "Nützliches Thema?",
  "endpoint": "/api/rating-useful.json",
  "avgRating": 2.4,
  "ratingType": "useful",
  "count": 12
}
  • Content:
    import h from 'hyperscript';
    import showTooltip from '../tooltip/tooltip';
    
    const LockState = {
      NO_LOCK: null,
      LOCKED: 'locked',
      UNLOCKED: 'unlocked',
    };
    
    const createValuesById = $inputs => (
      Array.from($inputs).reduce(
        (result, $input) => Object.assign(result, {
          [$input.id]: $input.value,
        }),
        {},
      )
    );
    
    export default class StarForm {
      constructor(element, submitForm) {
        this.$element = element;
        this.submitForm = submitForm;
        this.$fieldset = element.querySelector('.star-rating__fieldset');
        this.$count = element.querySelector('.star-rating__count');
        this.$starsWrapper = element.querySelector('.star-rating__stars');
        this.$inputs = element.querySelectorAll('.star-rating__input');
        this.$labels = element.querySelectorAll('.star-rating__label');
        this.$form = element.querySelector('.star-rating__form');
        this.lockState = LockState.NO_LOCK;
    
        this.id = element.dataset.itemId;
        this.avgRating = Math.round(Number(element.dataset.averageRating));
        this.ratingType = element.dataset.ratingType;
        this.endpoint = element.dataset.endpoint;
    
        this.localStorageId = `${this.id}-${this.ratingType}`;
        this.value = this.getStorage();
        this.valuesById = createValuesById(this.$inputs);
        this.removeUnlockedTooltip = null;
    
        this.init();
        this.initMediaListener();
      }
    
      getStorage() {
        return window.localStorage.getItem(this.localStorageId) || null;
      }
    
      setStorage(rating) {
        window.localStorage.setItem(this.localStorageId, rating);
      }
    
      setLockState(lockState) {
        this.lockState = lockState;
        this.$element.dataset.lockState = lockState;
      }
    
      setDisabled() {
        this.disabled = true;
        this.$inputs.forEach($input => $input.setAttribute('disabled', 'disabled'));
      }
    
      getValueByLabel($label) {
        const id = $label.getAttribute('for');
    
        return this.valuesById[id];
      }
    
      fillStars(rating) {
        this.$labels.forEach(($label, index) => {
          if (index + 1 <= rating) {
            $label.classList.add('star-rating__label--filled');
    
            return;
          }
    
          $label.classList.remove('star-rating__label--filled');
        });
      }
    
      handleCatcherClick() {
        if (this.lockState !== LockState.LOCKED) {
          return;
        }
    
        this.removeUnlockedTooltip = showTooltip(this.$fieldset, {
          text: 'Bitte Bewertung abgeben',
          color: 'blue-light',
        });
    
        this.setLockState(LockState.UNLOCKED);
      }
    
      handleInputChange(event) {
        const number = event.target.value;
        this.fillStars(number);
      }
    
      handleLabelClick(event) {
        if (this.disabled) {
          showTooltip(this.$fieldset, {
            text: 'Bewertung bereits abgegeben!',
            timeout: 3000,
          });
    
          return;
        }
    
        this.value = this.getValueByLabel(event.target);
        this.handleFormSubmit();
      }
    
      handleLabelMouseEnter(event) {
        if (this.disabled) {
          return;
        }
    
        const value = this.getValueByLabel(event.target);
    
        this.$element.classList.add('star-rating--hovered');
        this.fillStars(Number(value));
      }
    
      handleLabelMouseLeave() {
        this.$element.classList.remove('star-rating--hovered');
        this.fillStars(this.avgRating);
      }
    
      handleFormSubmit() {
        const rating = Number(this.value);
    
        this.setDisabled();
        this.setStorage(rating);
        this.setLockState(LockState.NO_LOCK);
    
        if (typeof this.removeUnlockedTooltip === 'function') {
          this.removeUnlockedTooltip();
        }
    
        showTooltip(this.$fieldset, {
          text: 'Danke für Ihre Bewertung!',
          timeout: 3000,
        });
    
        this.submitForm(this.id, rating, this.ratingType, this.endpoint, (data) => {
          this.$count.textContent = `(${data.count})`;
          this.avgRating = Math.round(data.averagerating);
          this.fillStars(this.avgRating);
        });
      }
    
      // Setup an extra unlock step for small viewports. We assume that small
      // viewports are touch devices and therefore can't visualize the hover state.
      initMediaListener() {
        const smallMediaTest = window.matchMedia('(max-width: 1024px)');
    
        const listener = (query) => {
          const lockState = query.matches && !this.disabled
            ? LockState.LOCKED
            : LockState.NO_LOCK;
    
          this.setLockState(lockState);
        };
    
        smallMediaTest.addListener(listener);
        listener(smallMediaTest);
      }
    
      init() {
        if (this.value) {
          this.setDisabled();
        }
    
        this.fillStars(this.avgRating);
    
        this.$starsWrapper.appendChild(
          h('div.star-rating__catcher', {
            onclick: () => this.handleCatcherClick(),
          }),
        );
    
        this.$form.addEventListener('submit', (event) => {
          event.preventDefault();
          this.handleFormSubmit();
        });
    
        this.$inputs.forEach(($input) => {
          $input.addEventListener('change', event => this.handleInputChange(event));
        });
    
        this.$labels.forEach(($label) => {
          $label.addEventListener('click', event => this.handleLabelClick(event));
          $label.addEventListener('mouseenter', event => this.handleLabelMouseEnter(event));
          $label.addEventListener('mouseleave', event => this.handleLabelMouseLeave(event));
        });
      }
    }
    
  • URL: /components/raw/star-rating/StarForm.js
  • Filesystem Path: src/components/atoms/star-rating/StarForm.js
  • Size: 5.1 KB
  • Content:
    import $ from 'jquery';
    import StarForm from './StarForm';
    
    function handleFormSubmit(articlepage, rating, ratingtype, url, success) {
      $.ajax({
        type: 'POST',
        url,
        data: {
          ratingtype,
          articlepage,
          rating,
        },
        success,
      });
    }
    
    document.querySelectorAll('.star-rating').forEach(($starRating) => {
      // eslint-disable-next-line
      new StarForm($starRating, handleFormSubmit);
    });
    
  • URL: /components/raw/star-rating/star-rating.js
  • Filesystem Path: src/components/atoms/star-rating/star-rating.js
  • Size: 415 Bytes
  • Content:
    @mixin star-rating-svg($color, $filled: false) {
      $fill: if($filled, $color, 'transparent');
    
      background-image: svg-url('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200"><path fill="#{$fill}" fill-rule="evenodd" stroke="#{$color}" stroke-width="12" d="M155.2 190.6L144.7 126l45-46.1-62.2-9.5L100 11.8 72.5 70.4l-62.1 9.5 45 46-10.6 64.7 55.2-30.5 55.2 30.5z"/></svg>');
    }
    
    .star-rating__input {
      opacity: 0;
      position: absolute;
      z-index: -1;
    }
    
    .star-rating__input:disabled + .star-rating__label {
      &:hover {
        cursor: default;
      }
    }
    
    .star-rating__label {
      @include star-rating-svg($color-blue-light);
      background-size: 2.5rem 2.5rem;
      display: inline-block;
      height: 2.5rem;
      margin-right: 0.4rem;
      outline: 0;
      width: 2.5rem;
    
      &:hover {
        cursor: pointer;
      }
    }
    
    .star-rating__label--filled {
      @include star-rating-svg($color-blue-light, true);
    }
    
    [data-lock-state='unlocked'] .star-rating__label {
      @include star-rating-svg($color-blue-light);
    }
    
    @include mq($from: 1025px) {
      .star-rating--hovered .star-rating__label {
        @include star-rating-svg($color-red);
      }
    
      .star-rating--hovered .star-rating__label--filled {
        @include star-rating-svg($color-red, true);
      }
    }
    
    .star-rating__label--outline {
      outline: 1px dotted #ccc;
    }
    
    .star-rating__input:focus + .star-rating__label {
      outline: 1px dotted #ccc;
    }
    
    .star-rating__inner {
      display: flex;
    }
    
    .star-rating__stars {
      position: relative;
    }
    
    .star-rating__catcher {
      display: none;
    
      [data-lock-state='locked'] & {
        bottom: 0;
        display: block;
        left: 0;
        position: absolute;
        right: 0;
        top: 0;
      }
    }
    
    .star-rating__count {
      color: $color-blue-light;
      display: inline-block;
      font-weight: bold;
      line-height: 2.5rem;
      margin-left: 0.4rem;
      vertical-align: top;
    }
    
    .star-rating__submit {
      display: none;
    }
    
    .star-rating__fieldset {
      border: 0;
      margin: 0;
      padding: 0;
      position: relative;
    }
    
  • URL: /components/raw/star-rating/star-rating.scss
  • Filesystem Path: src/components/atoms/star-rating/star-rating.scss
  • Size: 1.9 KB

There are no notes for this item.