-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathCRating.js
99 lines (97 loc) · 5.14 KB
/
CRating.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
/* jshint esversion: 6 */
/* global DOMError */
/*
Web Component `<c-rating name="" value="" use="" min="" max="">`
- Verze: 2021-06-09
- Zdroj: https://github.com/IndigoMultimediaTeam/CRatingElement
- `name`: je volitelné a určuje jména imputů (defaultně: `name_default+name_count++`)
- `disabled`: určuje aktivnost inputů (lze měnit i dynamicky po vytvoření)
- `value`: určuje počáteční hodnotu hodnocení (lze měnit i dynamicky po vytvoření)
- Zdroj možností
- určuje možnosti k hodnocení (resp. jejich popisky, `value` vždy odpovídá indexu od 0)
- `use`: ID elementu `<c-rating-buttons>…` obsahující textové popisky možností
- `min`/`max`: vygeneruje číselné štítky (např.: 1/3 → 1,2,3), defaultně 1/5
- případně načte texty potomků (samotné elementy smaže!)
- `.onchange`/událost `change` (analog. k inputům) vrací hodnotu konkrétního imputu (např.: `({ target })=> console.log(target.value)`)
- stylování (např.) `c-rating …; c-rating input …; c-rating input:checked + label …; c-rating label`
*/
(function CRating(d){
if(d.readyState==="loading") return d.addEventListener("DOMContentLoaded", CRating.bind(this, d));
const name_default= "CRating";
let name_count= 0;
class CRatingButtonsElement extends HTMLElement{
static get tag_name(){ return "c-rating-buttons"; }
get texts(){ return Array.from(this.children).map(({ innerText })=> innerText); }
constructor(){ super(); this.style.display= "none"; }
}
class CRatingElement extends HTMLElement{
static get tag_name(){ return "c-rating"; }
connectedCallback(){
if(!this.hasAttribute("name"))
this.setAttribute("name", name_default+name_count++);
this._state= 1; //≡"connected";
this._texts().map(this._createInput, this).forEach(el=> this.appendChild(el));
return this._refreshState();
}
/** @returns {string[]|number[]} */
_texts(){
if(this.hasAttribute("use")){
const el= d.getElementById(this.getAttribute("use"));
if(!(el instanceof CRatingButtonsElement))
throw new DOMError(`${el} is not instance of ${CRatingButtonsElement}`);
return el.texts;
}
if(this.hasAttribute("max")){
const [ min= 1, max= 5 ]= [ "min", "max" ].map(n=> Number(this.getAttribute(n)));
return Array.from({ length: max-min }).map((_, i)=> i+min);
}
const ch= Array.from(this.children);
const texts= ch.map(({ innerText })=> innerText);
ch.forEach(el=> el.remove());
return texts;
}
_createInput(text, value){
const /* fragment: `<><input><label><>`; name a id z hl. name */
el_fragment= d.createDocumentFragment(),
name= this.getAttribute("name"),
id= name+this.__inputs++;
const { disabled }= this;
el_fragment.appendChild(Object.assign(d.createElement("input"), {
type: "radio", checked: this.value===value,
onchange: this._changeEvent.bind(this),
id, name, value, disabled
}));
el_fragment.appendChild(Object.assign(d.createElement("label"), {
htmlFor: id, textContent: text
}));
return el_fragment;
}
_changeEvent(ev){
this.value= ev.target.value;
return this.dispatchEvent(new Event("onchange", ev));
}
/* propojeni html `value="…"` a JS `element.value` */
static get observedAttributes(){ return [ "value" ]; }
get value(){ return this.hasAttribute("value") ? Number(this.getAttribute("value")) : -1; }
set value(val){ return this.setAttribute("value", val); }
get name(){ return this.getAttribute("name"); }
set name(val){ return this.setAttribute("name", val); }
get disabled(){ return this.hasAttribute("disabled"); }
set disabled(bool){ return bool ? this.setAttribute("disabled", true) : this.removeAttribute("disabled"); }
attributeChangedCallback(_, val_old, val_new){
if(val_old===val_new) return false;
return this._refreshState();
}
/* propojeni s inputama */
_refreshState(){
if(!this._state) return 0;//krkolomnost kvuli tomu, ze attributeCh… se muze zavolat pred samotnym vytvorenim <c-rating>
const { value, disabled }= this;
Array.from(this.getElementsByTagName("input"))
.forEach((el, i)=> Object.assign(el, { disabled, checked: i===value }));//indexujeme od 0, takze OK
return 1;
}
constructor(){ super(); this.__inputs= 0; }
}
customElements.define(CRatingButtonsElement.tag_name, CRatingButtonsElement);
customElements.define(CRatingElement.tag_name, CRatingElement);
})(document);