Files
sigpro-ui/docs/components/rating.md
2026-04-02 19:31:39 +02:00

17 KiB

Rating

Star rating component with customizable count, icons, and read-only mode.

Tag

Rating

Props

Prop Type Default Description
value number | Signal<number> 0 Current rating value
count number | Signal<number> 5 Number of stars/items
mask string 'mask-star' Mask shape (mask-star, mask-star-2, mask-heart)
readonly boolean | Signal<boolean> false Disable interaction
class string '' Additional CSS classes (DaisyUI + Tailwind)

Live Examples

Basic Rating

Live Demo

const BasicDemo = () => {
  const rating = $(3);
  
  return Div({ class: 'flex flex-col gap-2 items-center' }, [
    Rating({
      value: rating,
      count: 5,
      onchange: (value) => rating(value)
    }),
    Div({ class: 'text-sm opacity-70' }, () => `Rating: ${rating()} / 5`)
  ]);
};
$mount(BasicDemo, '#demo-basic');

Heart Rating

Live Demo

const HeartDemo = () => {
  const rating = $(4);
  
  return Div({ class: 'flex flex-col gap-2 items-center' }, [
    Rating({
      value: rating,
      count: 5,
      mask: 'mask-heart',
      onchange: (value) => rating(value)
    }),
    Div({ class: 'text-sm opacity-70' }, () => `${rating()} hearts`)
  ]);
};
$mount(HeartDemo, '#demo-heart');

Star with Outline

Live Demo

const Star2Demo = () => {
  const rating = $(2);
  
  return Div({ class: 'flex flex-col gap-2 items-center' }, [
    Rating({
      value: rating,
      count: 5,
      mask: 'mask-star-2',
      onchange: (value) => rating(value)
    }),
    Div({ class: 'text-sm opacity-70' }, () => `${rating()} stars`)
  ]);
};
$mount(Star2Demo, '#demo-star2');

Read-only Rating

Live Demo

const ReadonlyDemo = () => {
  const rating = $(4.5);
  
  return Div({ class: 'flex flex-col gap-2 items-center' }, [
    Rating({
      value: rating,
      count: 5,
      readonly: true
    }),
    Div({ class: 'text-sm opacity-70' }, 'Average rating: 4.5/5 (read-only)')
  ]);
};
$mount(ReadonlyDemo, '#demo-readonly');

Custom Count

Live Demo

const CustomDemo = () => {
  const rating = $(3);
  const count = $(10);
  
  return Div({ class: 'flex flex-col gap-4 w-full' }, [
    Div({ class: 'flex items-center gap-4' }, [
      Span({ class: 'text-sm' }, 'Number of stars:'),
      Input({
        type: 'number',
        value: count,
        class: 'input input-sm w-24'
      })
    ]),
    Rating({
      value: rating,
      count: count,
      onchange: (value) => rating(value)
    }),
    Div({ class: 'text-sm opacity-70' }, () => `Rating: ${rating()} / ${count()}`)
  ]);
};
$mount(CustomDemo, '#demo-custom');

Product Review

Live Demo

const ReviewDemo = () => {
  const quality = $(4);
  const price = $(3);
  const support = $(5);
  
  const average = () => Math.round(((quality() + price() + support()) / 3) * 10) / 10;
  
  return Div({ class: 'flex flex-col gap-4' }, [
    Div({ class: 'text-lg font-bold' }, 'Product Review'),
    Div({ class: 'flex flex-col gap-2' }, [
      Div({ class: 'flex justify-between items-center' }, [
        Span({ class: 'text-sm w-24' }, 'Quality:'),
        Rating({
          value: quality,
          count: 5,
          size: 'sm',
          onchange: (v) => quality(v)
        })
      ]),
      Div({ class: 'flex justify-between items-center' }, [
        Span({ class: 'text-sm w-24' }, 'Price:'),
        Rating({
          value: price,
          count: 5,
          size: 'sm',
          onchange: (v) => price(v)
        })
      ]),
      Div({ class: 'flex justify-between items-center' }, [
        Span({ class: 'text-sm w-24' }, 'Support:'),
        Rating({
          value: support,
          count: 5,
          size: 'sm',
          onchange: (v) => support(v)
        })
      ])
    ]),
    Div({ class: 'divider my-1' }),
    Div({ class: 'flex justify-between items-center' }, [
      Span({ class: 'font-bold' }, 'Overall:'),
      Div({ class: 'text-2xl font-bold text-primary' }, () => average())
    ])
  ]);
};
$mount(ReviewDemo, '#demo-review');

All Variants

Live Demo

const VariantsDemo = () => {
  return Div({ class: 'flex flex-col gap-6' }, [
    Div({ class: 'text-center' }, [
      Div({ class: 'text-sm mb-2' }, 'Mask Star'),
      Rating({ value: $(3), count: 5, mask: 'mask-star' })
    ]),
    Div({ class: 'text-center' }, [
      Div({ class: 'text-sm mb-2' }, 'Mask Star 2 (yellow)' ),
      Rating({ value: $(4), count: 5, mask: 'mask-star-2', class: 'rating-warning' })
    ]),
    Div({ class: 'text-center' }, [
      Div({ class: 'text-sm mb-2' }, 'Mask Heart'),
      Rating({ value: $(5), count: 5, mask: 'mask-heart', class: 'rating-error' })
    ]),
    Div({ class: 'text-center' }, [
      Div({ class: 'text-sm mb-2' }, 'Half Stars (read-only)'),
      Rating({ value: $(3.5), count: 5, readonly: true })
    ])
  ]);
};
$mount(VariantsDemo, '#demo-variants');

Interactive Feedback

Live Demo

const FeedbackDemo = () => {
  const rating = $(0);
  const feedback = $(false);
  
  const messages = {
    1: 'Very disappointed 😞',
    2: 'Could be better 😕',
    3: 'Good 👍',
    4: 'Very good 😊',
    5: 'Excellent! 🎉'
  };
  
  return Div({ class: 'flex flex-col gap-4 items-center' }, [
    Div({ class: 'text-center' }, [
      Div({ class: 'text-sm mb-2' }, 'How was your experience?'),
      Rating({
        value: rating,
        count: 5,
        onchange: (value) => {
          rating(value);
          feedback(true);
          if (value >= 4) {
            Toast('Thank you for your positive feedback!', 'alert-success', 2000);
          } else if (value <= 2) {
            Toast('We appreciate your feedback and will improve!', 'alert-warning', 2000);
          } else {
            Toast('Thanks for your rating!', 'alert-info', 2000);
          }
        }
      })
    ]),
    () => rating() > 0 
      ? Div({ class: 'alert alert-soft text-center' }, [
          messages[rating()] || `Rating: ${rating()} stars`
        ])
      : null
  ]);
};
$mount(FeedbackDemo, '#demo-feedback');
<script> (function() { const initRatingExamples = () => { // 1. Basic Rating const basicTarget = document.querySelector('#demo-basic'); if (basicTarget && !basicTarget.hasChildNodes()) { const BasicDemo = () => { const rating = $(3); return Div({ class: 'flex flex-col gap-2 items-center' }, [ Rating({ value: rating, count: 5, onchange: (value) => rating(value) }), Div({ class: 'text-sm opacity-70' }, () => `Rating: ${rating()} / 5`) ]); }; $mount(BasicDemo, basicTarget); } // 2. Heart Rating const heartTarget = document.querySelector('#demo-heart'); if (heartTarget && !heartTarget.hasChildNodes()) { const HeartDemo = () => { const rating = $(4); return Div({ class: 'flex flex-col gap-2 items-center' }, [ Rating({ value: rating, count: 5, mask: 'mask-heart', onchange: (value) => rating(value) }), Div({ class: 'text-sm opacity-70' }, () => `${rating()} hearts`) ]); }; $mount(HeartDemo, heartTarget); } // 3. Star with Outline const star2Target = document.querySelector('#demo-star2'); if (star2Target && !star2Target.hasChildNodes()) { const Star2Demo = () => { const rating = $(2); return Div({ class: 'flex flex-col gap-2 items-center' }, [ Rating({ value: rating, count: 5, mask: 'mask-star-2', onchange: (value) => rating(value) }), Div({ class: 'text-sm opacity-70' }, () => `${rating()} stars`) ]); }; $mount(Star2Demo, star2Target); } // 4. Read-only Rating const readonlyTarget = document.querySelector('#demo-readonly'); if (readonlyTarget && !readonlyTarget.hasChildNodes()) { const ReadonlyDemo = () => { const rating = $(4.5); return Div({ class: 'flex flex-col gap-2 items-center' }, [ Rating({ value: rating, count: 5, readonly: true }), Div({ class: 'text-sm opacity-70' }, 'Average rating: 4.5/5 (read-only)') ]); }; $mount(ReadonlyDemo, readonlyTarget); } // 5. Custom Count const customTarget = document.querySelector('#demo-custom'); if (customTarget && !customTarget.hasChildNodes()) { const CustomDemo = () => { const rating = $(3); const count = $(10); return Div({ class: 'flex flex-col gap-4 w-full' }, [ Div({ class: 'flex items-center gap-4' }, [ Span({ class: 'text-sm' }, 'Number of stars:'), Input({ type: 'number', value: count, class: 'input input-sm w-24', oninput: (e) => count(parseInt(e.target.value) || 1) }) ]), Rating({ value: rating, count: count, onchange: (value) => rating(value) }), Div({ class: 'text-sm opacity-70' }, () => `Rating: ${rating()} / ${count()}`) ]); }; $mount(CustomDemo, customTarget); } // 6. Product Review const reviewTarget = document.querySelector('#demo-review'); if (reviewTarget && !reviewTarget.hasChildNodes()) { const ReviewDemo = () => { const quality = $(4); const price = $(3); const support = $(5); const average = () => Math.round(((quality() + price() + support()) / 3) * 10) / 10; return Div({ class: 'flex flex-col gap-4' }, [ Div({ class: 'text-lg font-bold' }, 'Product Review'), Div({ class: 'flex flex-col gap-2' }, [ Div({ class: 'flex justify-between items-center' }, [ Span({ class: 'text-sm w-24' }, 'Quality:'), Rating({ value: quality, count: 5, size: 'sm', onchange: (v) => quality(v) }) ]), Div({ class: 'flex justify-between items-center' }, [ Span({ class: 'text-sm w-24' }, 'Price:'), Rating({ value: price, count: 5, size: 'sm', onchange: (v) => price(v) }) ]), Div({ class: 'flex justify-between items-center' }, [ Span({ class: 'text-sm w-24' }, 'Support:'), Rating({ value: support, count: 5, size: 'sm', onchange: (v) => support(v) }) ]) ]), Div({ class: 'divider my-1' }), Div({ class: 'flex justify-between items-center' }, [ Span({ class: 'font-bold' }, 'Overall:'), Div({ class: 'text-2xl font-bold text-primary' }, () => average()) ]) ]); }; $mount(ReviewDemo, reviewTarget); } // 7. All Variants const variantsTarget = document.querySelector('#demo-variants'); if (variantsTarget && !variantsTarget.hasChildNodes()) { const VariantsDemo = () => { return Div({ class: 'flex flex-col gap-6' }, [ Div({ class: 'text-center' }, [ Div({ class: 'text-sm mb-2' }, 'Mask Star'), Rating({ value: $(3), count: 5, mask: 'mask-star' }) ]), Div({ class: 'text-center' }, [ Div({ class: 'text-sm mb-2' }, 'Mask Star 2 (yellow)' ), Rating({ value: $(4), count: 5, mask: 'mask-star-2', class: 'rating-warning' }) ]), Div({ class: 'text-center' }, [ Div({ class: 'text-sm mb-2' }, 'Mask Heart'), Rating({ value: $(5), count: 5, mask: 'mask-heart', class: 'rating-error' }) ]), Div({ class: 'text-center' }, [ Div({ class: 'text-sm mb-2' }, 'Half Stars (read-only)'), Rating({ value: $(3.5), count: 5, readonly: true }) ]) ]); }; $mount(VariantsDemo, variantsTarget); } // 8. Interactive Feedback const feedbackTarget = document.querySelector('#demo-feedback'); if (feedbackTarget && !feedbackTarget.hasChildNodes()) { const FeedbackDemo = () => { const rating = $(0); const feedback = $(false); const messages = { 1: 'Very disappointed 😞', 2: 'Could be better 😕', 3: 'Good 👍', 4: 'Very good 😊', 5: 'Excellent! 🎉' }; return Div({ class: 'flex flex-col gap-4 items-center' }, [ Div({ class: 'text-center' }, [ Div({ class: 'text-sm mb-2' }, 'How was your experience?'), Rating({ value: rating, count: 5, onchange: (value) => { rating(value); feedback(true); if (value >= 4) { Toast('Thank you for your positive feedback!', 'alert-success', 2000); } else if (value <= 2) { Toast('We appreciate your feedback and will improve!', 'alert-warning', 2000); } else { Toast('Thanks for your rating!', 'alert-info', 2000); } } }) ]), () => rating() > 0 ? Div({ class: 'alert alert-soft text-center' }, [ messages[rating()] || `Rating: ${rating()} stars` ]) : null ]); }; $mount(FeedbackDemo, feedbackTarget); } }; initRatingExamples(); if (window.$docsify) { window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => { hook.doneEach(initRatingExamples); }); } })(); </script>