386 lines
9.9 KiB
JavaScript
386 lines
9.9 KiB
JavaScript
class CCPInputWidgetController extends HTMLElement {
|
|
|
|
#input = null;
|
|
#renderer = null;
|
|
|
|
constructor(){
|
|
super()
|
|
this.#input = JSON.parse(this.getAttribute("input"))
|
|
this.#renderer = Renderer.instance(this.#input)
|
|
}
|
|
|
|
connectedCallback(){
|
|
console.log("Widget connected")
|
|
this.innerHTML = this.render()
|
|
this.#renderer.connectedCallback(this)
|
|
}
|
|
|
|
render(){
|
|
return this.#renderer.render()
|
|
}
|
|
|
|
get name(){
|
|
return this.#renderer.name
|
|
}
|
|
|
|
get value(){
|
|
return this.#renderer.getValue(this)
|
|
}
|
|
|
|
set value(v){
|
|
return this.#renderer.setValue(this, v)
|
|
}
|
|
}
|
|
window.customElements.define('d4s-ccp-input', CCPInputWidgetController);
|
|
|
|
class Renderer{
|
|
|
|
#input = null;
|
|
|
|
constructor(input){
|
|
this.#input = input
|
|
}
|
|
|
|
connectedCallback(controller){
|
|
|
|
}
|
|
|
|
get schema(){
|
|
return this.#input.schema
|
|
}
|
|
|
|
get name(){
|
|
return this.#input.id
|
|
}
|
|
|
|
get title(){
|
|
return this.#input.title
|
|
}
|
|
|
|
get description(){
|
|
return this.#input.description
|
|
}
|
|
|
|
get required(){
|
|
return this.#input.minOccurs > 0
|
|
}
|
|
|
|
get readOnly(){
|
|
return this.#input.schema.readOnly
|
|
}
|
|
|
|
static instance(input){
|
|
if(this.isEnum(input)){
|
|
return new EnumInputRenderer(input)
|
|
}
|
|
if(this.isCode(input)){
|
|
return new CodeInputRenderer(input)
|
|
}
|
|
if(this.isDateTime(input)){
|
|
return new DateTimeInputRenderer(input)
|
|
}
|
|
if(this.isSecret(input)){
|
|
return new SecretInputRenderer(input)
|
|
}
|
|
if(this.isFile(input)){
|
|
return new FileInputRenderer(input)
|
|
}
|
|
return new SimpleInputRenderer(input)
|
|
}
|
|
|
|
static isEnum(input){
|
|
return (input.schema.type === "string") && ("enum" in input.schema)
|
|
}
|
|
|
|
static isSecret(input){
|
|
return (input.schema.type === "string") &&
|
|
("format" in input.schema) &&
|
|
(input.schema.format != null) &&
|
|
(input.schema.format.toLowerCase() === "secret")
|
|
}
|
|
|
|
static isCode(input){
|
|
return (input.schema.type === "string") &&
|
|
("format" in input.schema) &&
|
|
(input.schema.format != null) &&
|
|
(input.schema.format.toLowerCase() === "code")
|
|
}
|
|
|
|
static isFile(input){
|
|
return (input.schema.type === "string") &&
|
|
("format" in input.schema) &&
|
|
(input.schema.format != null) &&
|
|
(input.schema.format.toLowerCase() === "file")
|
|
}
|
|
|
|
static isDateTime(input){
|
|
return (input.schema.type === "string") &&
|
|
("format" in input.schema) &&
|
|
(input.schema.format != null) &&
|
|
(["date", "time", "datetime"].indexOf(input.schema.format.toLowerCase()) !== -1)
|
|
}
|
|
}
|
|
|
|
class SimpleInputRenderer extends Renderer{
|
|
|
|
#html = null;
|
|
|
|
constructor(input){
|
|
super(input)
|
|
}
|
|
|
|
getValue(parent){
|
|
return parent.querySelector("input").value
|
|
}
|
|
|
|
setValue(parent, v){
|
|
parent.querySelector("input").value = v
|
|
}
|
|
|
|
render(){
|
|
let required = this.required ? 'required="required"' : ""
|
|
let readonly = this.readOnly ? 'readonly="readOnly"' : ""
|
|
this.#html = `
|
|
<div class="ccp-input-widget form-field">
|
|
<label>
|
|
${this.title}
|
|
<span class="ccp-help-icon" title="${this.description}" alt="${this.description}">?</span>
|
|
</label>
|
|
<input class="ccp-input-widget form-control" name="${this.name}" value="${this.schema.default}" ${required} ${readonly}></input>
|
|
<span style="user-select:none;position:relative;top:-1.6rem;left:95%;cursor:pointer" name="password_toggle" class="d-none">👁</span>
|
|
</div>
|
|
`
|
|
return this.#html
|
|
}
|
|
}
|
|
|
|
class FileInputRenderer extends Renderer{
|
|
|
|
#html = null;
|
|
#content = null;
|
|
|
|
constructor(input){
|
|
super(input)
|
|
}
|
|
|
|
connectedCallback(controller){
|
|
controller.querySelector(`input[name=${this.name}]`).addEventListener("change", ev=>{
|
|
const tgt = ev.target
|
|
const ename = tgt.getAttribute("name")
|
|
if(ename === this.name){
|
|
const file = ev.target.files[0]
|
|
if(file.type !== this.schema.contentMediaType){
|
|
alert("Unsupported media type. Must be " + this.schema.contentMediaType)
|
|
ev.stopPropagation()
|
|
ev.preventDefault()
|
|
tgt.value = null
|
|
return false
|
|
}
|
|
if(file.size > 100*1024){
|
|
alert("This input allows only small files (100K). Use references instead ")
|
|
ev.stopPropagation()
|
|
ev.preventDefault()
|
|
tgt.value = null
|
|
return false
|
|
}
|
|
const reader = new FileReader()
|
|
reader.addEventListener('load', ev=>{
|
|
let encoded = ev.target.result.toString().replace(/^data:(.*,)?/, '');
|
|
if ((encoded.length % 4) > 0) {
|
|
encoded += '='.repeat(4 - (encoded.length % 4));
|
|
}
|
|
this.#content = encoded
|
|
})
|
|
reader.readAsDataURL(file)
|
|
}
|
|
})
|
|
}
|
|
|
|
getValue(parent){
|
|
return this.#content
|
|
}
|
|
|
|
setValue(parent, v){
|
|
parent.querySelector("input").value = v
|
|
}
|
|
|
|
render(){
|
|
let required = this.required ? 'required="required"' : ""
|
|
let readonly = this.readOnly ? 'readonly="readOnly"' : ""
|
|
this.#html = `
|
|
<div class="ccp-input-widget form-field">
|
|
<label>
|
|
${this.title}
|
|
<span class="ccp-help-icon" title="${this.description}" alt="${this.description}">?</span>
|
|
</label>
|
|
<input type="file" class="ccp-input-widget form-control" name="${this.name}" value="${this.schema.default}" ${required} ${readonly}></input>
|
|
</div>
|
|
`
|
|
return this.#html
|
|
}
|
|
}
|
|
|
|
class SecretInputRenderer extends Renderer{
|
|
|
|
#html = null;
|
|
|
|
constructor(input){
|
|
super(input)
|
|
}
|
|
|
|
getValue(parent){
|
|
return parent.querySelector("input").value
|
|
}
|
|
|
|
setValue(parent, v){
|
|
parent.querySelector("input").value = v
|
|
}
|
|
|
|
connectedCallback(controller){
|
|
controller.addEventListener("click", ev=>{
|
|
const ename = ev.target.getAttribute("name")
|
|
if(ename === "password_toggle"){
|
|
const w = controller.querySelector("div.ccp-input-widget input")
|
|
w.type = (w.type === "password" ? "" : "password")
|
|
ev.preventDefault()
|
|
}
|
|
})
|
|
}
|
|
|
|
render(){
|
|
let required = this.required ? 'required="required"' : ""
|
|
let readonly = this.readOnly ? 'readonly="readOnly"' : ""
|
|
this.#html = `
|
|
<div class="ccp-input-widget ccp-input-widget form-field">
|
|
<label>
|
|
${this.title}
|
|
<span class="ccp-help-icon" title="${this.description}" alt="${this.description}">?</span>
|
|
</label>
|
|
<input type="password" class="ccp-input-widget form-control" name="${this.name}" value="${this.schema.default}" ${required} ${readonly}></input>
|
|
<span style="user-select:none;position:relative;top:-1.6rem;float:right;cursor:pointer" name="password_toggle">👁</span>
|
|
</div>
|
|
`
|
|
return this.#html
|
|
}
|
|
}
|
|
|
|
class DateTimeInputRenderer extends Renderer{
|
|
|
|
#html = null;
|
|
|
|
constructor(input){
|
|
super(input)
|
|
}
|
|
|
|
getValue(parent){
|
|
return parent.querySelector("input").value
|
|
}
|
|
|
|
setValue(parent, v){
|
|
parent.querySelector("input").value = v
|
|
}
|
|
|
|
render(){
|
|
let required = this.required ? 'required="required"' : ""
|
|
let readonly = this.schema.readOnly ? 'readonly="readOnly"' : ""
|
|
let t = this.schema.format.toLowerCase() === "datetime" ? "datetime-local" : this.schema.format.toLowerCase()
|
|
this.#html = `
|
|
<div class="ccp-input-widget form-field">
|
|
<label>
|
|
${this.title}
|
|
<span class="ccp-help-icon" title="${this.description}" alt="${this.description}">?</span>
|
|
</label>
|
|
<input type="${t}" class="ccp-input-widget form-control" name="${this.name}" value="${this.schema.default}" ${required} ${readonly}></input>
|
|
</div>
|
|
`
|
|
return this.#html
|
|
}
|
|
}
|
|
|
|
class EnumInputRenderer extends Renderer{
|
|
|
|
#html = null;
|
|
|
|
constructor(input){
|
|
super(input)
|
|
}
|
|
|
|
getValue(parent){
|
|
return parent.querySelector("select").value
|
|
}
|
|
|
|
setValue(parent, v){
|
|
parent.querySelector("select").value = v
|
|
}
|
|
|
|
render(){
|
|
let options = this.schema.enum.map(e => {
|
|
return e === this.schema.default ?
|
|
`<option name="${e}" value="${e}" selected="selected">${e}</option>` :
|
|
`<option name="${e}" value="${e}">${e}</option>`
|
|
})
|
|
let required = this.required ? 'required="required"' : ""
|
|
let readonly = this.schema.readOnly ? 'readonly="readOnly"' : ""
|
|
this.#html = `
|
|
<div class="ccp-input-widget form-field">
|
|
<label>
|
|
${this.title}
|
|
<span class="ccp-help-icon" title="${this.description}" alt="${this.description}">?</span>
|
|
</label>
|
|
<select class="ccp-input-widget form-control" name="${this.name}" value="${this.schema.default}" ${required}>
|
|
${options.join("")}
|
|
</select>
|
|
</div>
|
|
`
|
|
return this.#html
|
|
}
|
|
}
|
|
|
|
class CodeInputRenderer extends Renderer{
|
|
|
|
#html = null;
|
|
#codemirror = null;
|
|
|
|
constructor(input){
|
|
super(input)
|
|
}
|
|
|
|
getValue(parent){
|
|
return parent.querySelector("textarea").value
|
|
}
|
|
|
|
setValue(parent, v){
|
|
parent.querySelector("textarea").value = v
|
|
}
|
|
|
|
connectedCallback(controller){
|
|
/*const ta = controller.querySelector("textarea")
|
|
const opts = {
|
|
lineNumbers: true,
|
|
indentUnit: 4,
|
|
matchBrackets: true,
|
|
mode: this.schema.contentMediaType,
|
|
readOnly : this.schema.readOnly ? true : false
|
|
}
|
|
|
|
this.#codemirror = CodeMirror.fromTextArea(ta, opts)
|
|
this.#codemirror.setValue(this.schema.default)
|
|
this.#codemirror.refresh()*/
|
|
}
|
|
|
|
render(){
|
|
let required = this.required ? 'required="required"' : ""
|
|
let readonly = this.schema.readOnly ? 'readonly="readOnly"' : ""
|
|
this.#html = `
|
|
<div class="ccp-input-widget form-field">
|
|
<label>
|
|
${this.title}
|
|
<span class="ccp-help-icon" title="${this.description}" alt="${this.description}">?</span>
|
|
</label>
|
|
<textarea class="ccp-input-widget form-control" ${required} ${readonly}>${this.schema.default}</textarea>
|
|
</div>
|
|
`
|
|
return this.#html
|
|
}
|
|
}
|