188 lines
3.3 KiB
Text
188 lines
3.3 KiB
Text
// templui component rating - version: v0.84.0 installed by templui v0.84.0
|
|
package rating
|
|
|
|
import (
|
|
"fmt"
|
|
"git.jmbit.de/jmb/scanfile/server/web/templui/components/icon"
|
|
"git.jmbit.de/jmb/scanfile/server/web/templui/utils"
|
|
"strconv"
|
|
)
|
|
|
|
type Style string
|
|
|
|
const (
|
|
StyleStar Style = "star"
|
|
StyleHeart Style = "heart"
|
|
StyleEmoji Style = "emoji"
|
|
)
|
|
|
|
type Props struct {
|
|
ID string
|
|
Class string
|
|
Attributes templ.Attributes
|
|
Value float64
|
|
ReadOnly bool
|
|
Precision float64
|
|
Name string
|
|
OnlyInteger bool
|
|
}
|
|
|
|
type GroupProps struct {
|
|
ID string
|
|
Class string
|
|
Attributes templ.Attributes
|
|
}
|
|
|
|
type ItemProps struct {
|
|
ID string
|
|
Class string
|
|
Attributes templ.Attributes
|
|
Value int
|
|
Style Style
|
|
}
|
|
|
|
templ Rating(props ...Props) {
|
|
{{ var p Props }}
|
|
if len(props) > 0 {
|
|
{{ p = props[0] }}
|
|
}
|
|
{{ p.setDefaults() }}
|
|
<div
|
|
if p.ID != "" {
|
|
id={ p.ID }
|
|
}
|
|
data-rating-component
|
|
data-initial-value={ fmt.Sprintf("%.2f", p.Value) }
|
|
data-precision={ fmt.Sprintf("%.2f", p.Precision) }
|
|
data-readonly={ strconv.FormatBool(p.ReadOnly) }
|
|
if p.Name != "" {
|
|
data-name={ p.Name }
|
|
}
|
|
data-onlyinteger={ strconv.FormatBool(p.OnlyInteger) }
|
|
class={
|
|
utils.TwMerge(
|
|
"flex flex-col items-start gap-1",
|
|
p.Class,
|
|
),
|
|
}
|
|
{ p.Attributes... }
|
|
>
|
|
{ children... }
|
|
if p.Name != "" {
|
|
<input
|
|
type="hidden"
|
|
name={ p.Name }
|
|
value={ fmt.Sprintf("%.2f", p.Value) }
|
|
data-rating-input
|
|
/>
|
|
}
|
|
</div>
|
|
}
|
|
|
|
templ Group(props ...GroupProps) {
|
|
{{ var p GroupProps }}
|
|
if len(props) > 0 {
|
|
{{ p = props[0] }}
|
|
}
|
|
<div
|
|
if p.ID != "" {
|
|
id={ p.ID }
|
|
}
|
|
class={ utils.TwMerge("flex flex-row items-center gap-1", p.Class) }
|
|
{ p.Attributes... }
|
|
>
|
|
{ children... }
|
|
</div>
|
|
}
|
|
|
|
templ Item(props ...ItemProps) {
|
|
{{ var p ItemProps }}
|
|
if len(props) > 0 {
|
|
{{ p = props[0] }}
|
|
}
|
|
{{ p.setDefaults() }}
|
|
<div
|
|
if p.ID != "" {
|
|
id={ p.ID }
|
|
}
|
|
data-rating-item
|
|
data-rating-value={ strconv.Itoa(p.Value) }
|
|
class={
|
|
utils.TwMerge(
|
|
"relative",
|
|
colorClass(p.Style),
|
|
"transition-opacity",
|
|
"cursor-pointer", // Default cursor
|
|
p.Class,
|
|
),
|
|
}
|
|
{ p.Attributes... }
|
|
>
|
|
<div class="opacity-30">
|
|
@ratingIcon(p.Style, false, float64(p.Value))
|
|
</div>
|
|
<div
|
|
class="absolute inset-0 overflow-hidden w-0"
|
|
data-rating-item-foreground
|
|
>
|
|
@ratingIcon(p.Style, true, float64(p.Value))
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
func colorClass(style Style) string {
|
|
switch style {
|
|
case StyleHeart:
|
|
return "text-destructive"
|
|
case StyleEmoji:
|
|
return "text-yellow-500"
|
|
default:
|
|
return "text-yellow-400"
|
|
}
|
|
}
|
|
|
|
func ratingIcon(style Style, filled bool, value float64) templ.Component {
|
|
if style == StyleEmoji {
|
|
if filled {
|
|
switch {
|
|
case value <= 1:
|
|
return icon.Angry()
|
|
case value <= 2:
|
|
return icon.Frown()
|
|
case value <= 3:
|
|
return icon.Meh()
|
|
case value <= 4:
|
|
return icon.Smile()
|
|
default:
|
|
return icon.Laugh()
|
|
}
|
|
}
|
|
return icon.Meh()
|
|
}
|
|
iconProps := icon.Props{}
|
|
if filled {
|
|
iconProps.Fill = "currentColor"
|
|
}
|
|
switch style {
|
|
case StyleHeart:
|
|
return icon.Heart(iconProps)
|
|
default:
|
|
return icon.Star(iconProps)
|
|
}
|
|
}
|
|
|
|
func (p *ItemProps) setDefaults() {
|
|
if p.Style == "" {
|
|
p.Style = StyleStar
|
|
}
|
|
}
|
|
|
|
func (p *Props) setDefaults() {
|
|
if p.Precision <= 0 {
|
|
p.Precision = 1.0
|
|
}
|
|
}
|
|
|
|
templ Script() {
|
|
<script defer src="/assets/js/rating.min.js"></script>
|
|
}
|