Merge pull request 'ccp-features' (#10) from ccp-features into master
Reviewed-on: gCubeSystem/cdn-experiments#10
This commit is contained in:
commit
e45228b996
|
@ -61,7 +61,7 @@ class CCPExecutionForm extends HTMLElement{
|
||||||
<h5>Inputs</h5>
|
<h5>Inputs</h5>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body ccp-inputs">
|
<div class="card-body ccp-inputs">
|
||||||
<div class="card-body">
|
<div>
|
||||||
<div class="form-group"></div>
|
<div class="form-group"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,181 +1,382 @@
|
||||||
class CCPInputWidgetController extends HTMLElement {
|
class CCPInputWidgetController extends HTMLElement {
|
||||||
|
|
||||||
#input = null;
|
#data = null;
|
||||||
#renderer = null;
|
|
||||||
|
|
||||||
constructor(){
|
constructor(){
|
||||||
super()
|
super()
|
||||||
this.#input = JSON.parse(this.getAttribute("input"))
|
|
||||||
this.#renderer = Renderer.instance(this.#input)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback(){
|
connectedCallback(){
|
||||||
console.log("Widget connected")
|
this.#data = JSON.parse(this.getAttribute("input"))
|
||||||
this.innerHTML = this.render()
|
|
||||||
this.#renderer.connectedCallback(this)
|
if(this.isChecklist()){
|
||||||
|
|
||||||
|
const opts = this.#data.schema.enum.join(",")
|
||||||
|
this.innerHTML += `<d4s-ccp-input-checklist readonly="${this.#data.schema.readOnly}" options="${opts}" default="${this.#data.schema.default||''}" maxOccurs="${this.#data.maxOccurs||1}" minOccurs="${this.#data.minOccurs||1}" name="${this.#data.id}" description="${this.#data.description}" title="${this.#data.title}"></d4s-ccp-input-checklist>`
|
||||||
|
|
||||||
|
} else if(this.isEnum()){
|
||||||
|
|
||||||
|
const opts = this.#data.schema.enum.join(",")
|
||||||
|
this.innerHTML += `<d4s-ccp-input-enum readonly="${this.#data.schema.readOnly}" options="${opts}" default="${this.#data.schema.default||''}" maxOccurs="${this.#data.maxOccurs||1}" minOccurs="${this.#data.minOccurs||1}" name="${this.#data.id}" description="${this.#data.description}" title="${this.#data.title}"></d4s-ccp-input-enum>`
|
||||||
|
}else if(this.isCode()){
|
||||||
|
this.innerHTML += `<d4s-ccp-input-textarea readonly="${this.#data.schema.readOnly}" default="${this.#data.schema.default||''}" maxOccurs="${this.#data.maxOccurs||1}" minOccurs="${this.#data.minOccurs||1}" name="${this.#data.id}" description="${this.#data.description}" title="${this.#data.title}"></d4s-ccp-input-textarea>`
|
||||||
|
|
||||||
|
}else if(this.isDateTime()){
|
||||||
|
const t = this.#data.schema.format.toLowerCase() === "datetime" ?
|
||||||
|
"datetime-local" : this.#data.schema.format.toLowerCase()
|
||||||
|
this.innerHTML += `<d4s-ccp-input-simple type="${t}" readonly="${this.#data.schema.readOnly}" default="${this.#data.schema.default||''}" maxOccurs="${this.#data.maxOccurs||1}" minOccurs="${this.#data.minOccurs||1}" name="${this.#data.id}" description="${this.#data.description}" title="${this.#data.title}"></d4s-ccp-input-simple>`
|
||||||
|
|
||||||
|
}else if(this.isFile()){
|
||||||
|
this.innerHTML += `<d4s-ccp-input-file readonly="${this.#data.schema.readOnly}" default="${this.#data.schema.default||''}" maxOccurs="${this.#data.maxOccurs||1}" minOccurs="${this.#data.minOccurs||1}" name="${this.#data.id}" description="${this.#data.description}" title="${this.#data.title}"></d4s-ccp-input-file>`
|
||||||
|
|
||||||
|
}else if(this.isSecret()){
|
||||||
|
this.innerHTML += `<d4s-ccp-input-secret readonly="${this.#data.schema.readOnly}" default="${this.#data.schema.default||''}" maxOccurs="${this.#data.maxOccurs||1}" minOccurs="${this.#data.minOccurs||1}" name="${this.#data.id}" description="${this.#data.description}" title="${this.#data.title}"></d4s-ccp-input-secret>`
|
||||||
|
|
||||||
|
}else{
|
||||||
|
this.innerHTML += `<d4s-ccp-input-simple readonly="${this.#data.schema.readOnly}" default="${this.#data.schema.default||''}" maxOccurs="${this.#data.maxOccurs||1}" minOccurs="${this.#data.minOccurs||1}" name="${this.#data.id}" description="${this.#data.description}" title="${this.#data.title}"></d4s-ccp-input-simple>`
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render(){
|
set value(value){
|
||||||
return this.#renderer.render()
|
this.firstElementChild.value = value
|
||||||
}
|
|
||||||
|
|
||||||
get name(){
|
|
||||||
return this.#renderer.name
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get value(){
|
get value(){
|
||||||
return this.#renderer.getValue(this)
|
return this.firstElementChild.value.length === 1 ?
|
||||||
|
this.firstElementChild.value[0] :
|
||||||
|
this.firstElementChild.value
|
||||||
}
|
}
|
||||||
|
|
||||||
set value(v){
|
get name(){
|
||||||
return this.#renderer.setValue(this, v)
|
return this.firstElementChild.name
|
||||||
|
}
|
||||||
|
|
||||||
|
isChecklist(){
|
||||||
|
return this.isEnum() && (this.#data.schema.format === "checklist")
|
||||||
|
}
|
||||||
|
|
||||||
|
isEnum(){
|
||||||
|
return (this.#data.schema.type === "string") && ("enum" in this.#data.schema)
|
||||||
|
}
|
||||||
|
|
||||||
|
isSecret(){
|
||||||
|
return (this.#data.schema.type === "string") &&
|
||||||
|
("format" in this.#data.schema) &&
|
||||||
|
(this.#data.schema.format != null) &&
|
||||||
|
(this.#data.schema.format.toLowerCase() === "secret")
|
||||||
|
}
|
||||||
|
|
||||||
|
isCode(){
|
||||||
|
return (this.#data.schema.type === "string") &&
|
||||||
|
("format" in this.#data.schema) &&
|
||||||
|
(this.#data.schema.format != null) &&
|
||||||
|
(this.#data.schema.format.toLowerCase() === "code")
|
||||||
|
}
|
||||||
|
|
||||||
|
isFile(){
|
||||||
|
return (this.#data.schema.type === "string") &&
|
||||||
|
("format" in this.#data.schema) &&
|
||||||
|
(this.#data.schema.format != null) &&
|
||||||
|
(this.#data.schema.format.toLowerCase() === "file")
|
||||||
|
}
|
||||||
|
|
||||||
|
isDateTime(){
|
||||||
|
return (this.#data.schema.type === "string") &&
|
||||||
|
("format" in this.#data.schema) &&
|
||||||
|
(this.#data.schema.format != null) &&
|
||||||
|
(["date", "time", "datetime"].indexOf(this.#data.schema.format.toLowerCase()) !== -1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
window.customElements.define('d4s-ccp-input', CCPInputWidgetController);
|
window.customElements.define('d4s-ccp-input', CCPInputWidgetController);
|
||||||
|
|
||||||
class Renderer{
|
class CCPBaseInputWidgetController extends HTMLElement{
|
||||||
|
#rootdoc = null;
|
||||||
|
#minOccurs = 1;
|
||||||
|
#maxOccurs = 1;
|
||||||
|
#description = ""
|
||||||
|
#title = ""
|
||||||
|
#name = ""
|
||||||
|
#default = null
|
||||||
|
#options = null
|
||||||
|
#value = null
|
||||||
|
#readonly = false
|
||||||
|
|
||||||
#input = null;
|
//useful to avoid having a custom element for every basic input type
|
||||||
|
#type = null
|
||||||
|
|
||||||
constructor(input){
|
#count = 1
|
||||||
this.#input = input
|
|
||||||
|
constructor(){
|
||||||
|
super()
|
||||||
|
this.#rootdoc = this//this.attachShadow({ mode: "open" });
|
||||||
|
this.#name = this.getAttribute("name")
|
||||||
|
this.#title = this.getAttribute("title")
|
||||||
|
this.#description = this.getAttribute("description")
|
||||||
|
this.#default = this.getAttribute("default")
|
||||||
|
this.#minOccurs = Number(this.getAttribute("minoccurs") ? this.getAttribute("minoccurs") : 1)
|
||||||
|
this.#maxOccurs = Number(this.getAttribute("maxoccurs") ? this.getAttribute("maxoccurs") : 1)
|
||||||
|
this.#readonly = this.getAttribute("readonly") === "true"
|
||||||
|
|
||||||
|
// coalesce all basic input types
|
||||||
|
this.#type = this.getAttribute("type")
|
||||||
|
|
||||||
|
// Handle enum case
|
||||||
|
this.#options = (this.getAttribute("options") ? this.getAttribute("options").split(",") : null)
|
||||||
|
|
||||||
|
this.value = Array(Math.max(this.#minOccurs,1)).fill(this.#default)
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback(controller){
|
setValue(v){
|
||||||
|
this.#value = v
|
||||||
|
this.render()
|
||||||
}
|
}
|
||||||
|
|
||||||
get schema(){
|
set value(v){
|
||||||
return this.#input.schema
|
const actual = Array.isArray(v) ? v : [v]
|
||||||
|
if(actual.length < this.#minOccurs || actual.length > this.#maxOccurs){
|
||||||
|
throw `Value with length ${v.length} does not respect bounds [${this.minOccurs},${this.maxOccurs}]`
|
||||||
|
}
|
||||||
|
this.#value = actual
|
||||||
|
this.render()
|
||||||
}
|
}
|
||||||
|
|
||||||
get name(){
|
get value(){
|
||||||
return this.#input.id
|
return this.#value
|
||||||
}
|
}
|
||||||
|
|
||||||
get title(){
|
get rootdoc(){
|
||||||
return this.#input.title
|
return this.#rootdoc
|
||||||
}
|
|
||||||
|
|
||||||
get description(){
|
|
||||||
return this.#input.description
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get required(){
|
get required(){
|
||||||
return this.#input.minOccurs > 0
|
return this.#minOccurs > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
get readOnly(){
|
get readonly(){
|
||||||
return this.#input.schema.readOnly
|
return this.#readonly
|
||||||
}
|
}
|
||||||
|
|
||||||
static instance(input){
|
isIncrementable(){
|
||||||
if(this.isEnum(input)){
|
return this.#value.length < this.#maxOccurs
|
||||||
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){
|
isDecrementable(){
|
||||||
return (input.schema.type === "string") && ("enum" in input.schema)
|
return this.#value.length > this.#minOccurs && this.#value.length > 1
|
||||||
}
|
}
|
||||||
|
|
||||||
static isSecret(input){
|
get count(){
|
||||||
return (input.schema.type === "string") &&
|
return this.#count
|
||||||
("format" in input.schema) &&
|
|
||||||
(input.schema.format != null) &&
|
|
||||||
(input.schema.format.toLowerCase() === "secret")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static isCode(input){
|
get name(){
|
||||||
return (input.schema.type === "string") &&
|
return this.#name
|
||||||
("format" in input.schema) &&
|
|
||||||
(input.schema.format != null) &&
|
|
||||||
(input.schema.format.toLowerCase() === "code")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static isFile(input){
|
get title(){
|
||||||
return (input.schema.type === "string") &&
|
return this.#title
|
||||||
("format" in input.schema) &&
|
|
||||||
(input.schema.format != null) &&
|
|
||||||
(input.schema.format.toLowerCase() === "file")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static isDateTime(input){
|
get description(){
|
||||||
return (input.schema.type === "string") &&
|
return this.#description
|
||||||
("format" in input.schema) &&
|
|
||||||
(input.schema.format != null) &&
|
|
||||||
(["date", "time", "datetime"].indexOf(input.schema.format.toLowerCase()) !== -1)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class SimpleInputRenderer extends Renderer{
|
get default(){
|
||||||
|
return this.#default
|
||||||
#html = null;
|
|
||||||
|
|
||||||
constructor(input){
|
|
||||||
super(input)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getValue(parent){
|
get options(){
|
||||||
return parent.querySelector("input").value
|
return this.#options
|
||||||
}
|
}
|
||||||
|
|
||||||
setValue(parent, v){
|
set default(v){
|
||||||
parent.querySelector("input").value = v
|
this.#default = v
|
||||||
|
}
|
||||||
|
|
||||||
|
get type(){
|
||||||
|
return this.#type
|
||||||
}
|
}
|
||||||
|
|
||||||
render(){
|
render(){
|
||||||
let required = this.required ? 'required="required"' : ""
|
this.rootdoc.innerHTML = `
|
||||||
let readonly = this.readOnly ? 'readonly="readOnly"' : ""
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
this.#html = `
|
<div name="root" class="my-3 ccp-input-widget form-field row">
|
||||||
<div class="ccp-input-widget form-field">
|
<label class="form-label">
|
||||||
<label>
|
${this.required ? `<span title="Required" class="p-1 text-danger">*</span>` : ``}
|
||||||
${this.title}
|
${this.title}
|
||||||
<span class="ccp-help-icon" title="${this.description}" alt="${this.description}">?</span>
|
<span class="badge text-primary" title="${this.#description}" alt="${this.#description}">?</span>
|
||||||
|
<div style="float:right">
|
||||||
|
${this.isIncrementable() ? `<span name="plus" title="add one" class="badge text-success border-success btn">+</span>` : ``}
|
||||||
|
${this.isDecrementable() ? `<span name="minus" title="remove one" class="badge border border-danger text-danger btn">-</span>` : ``}
|
||||||
|
</div>
|
||||||
|
${this.content()}
|
||||||
</label>
|
</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>
|
</div>
|
||||||
`
|
`
|
||||||
return this.#html
|
this.#rootdoc.querySelector("div[name=root]").addEventListener("click", ev=>{
|
||||||
|
const src = ev.target.getAttribute("name")
|
||||||
|
if(src === "plus"){
|
||||||
|
this.#value.push(this.#default)
|
||||||
|
this.render()
|
||||||
|
}else if(src === "minus"){
|
||||||
|
this.#value.pop()
|
||||||
|
this.render()
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class FileInputRenderer extends Renderer{
|
class CCPSimpleInputWidgetController extends CCPBaseInputWidgetController{
|
||||||
|
|
||||||
#html = null;
|
constructor(){
|
||||||
#content = null;
|
super()
|
||||||
|
|
||||||
constructor(input){
|
|
||||||
super(input)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback(controller){
|
connectedCallback(){
|
||||||
controller.querySelector(`input[name=${this.name}]`).addEventListener("change", ev=>{
|
this.rootdoc.addEventListener("input", ev=>{
|
||||||
|
if(ev.target.getAttribute("name") === this.name){
|
||||||
|
const index = Number(ev.target.getAttribute("data-index"))
|
||||||
|
this.value[index] = ev.target.value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
content(){
|
||||||
|
if(this.value.length <= 1){
|
||||||
|
return `<input data-index="0" value="${this.value.length ? this.value[0] : ''}" class="my-2 form-control" placeholder="${this.title}" type="${this.type}" name="${this.name}" ${this.readonly ? 'readonly' : ''} ${this.required ? 'required' : ''}/>`
|
||||||
|
}
|
||||||
|
var out =
|
||||||
|
this.value.map((c,i)=>{
|
||||||
|
return `
|
||||||
|
<input name="${this.name}" value="${c}" data-index="${i}" class="ccp-input my-2 form-control" placeholder="${this.title}" type="${this.type}" name="${this.name}" ${this.readonly ? 'readonly' : ''} ${this.required ? 'required' : ''}/>
|
||||||
|
`
|
||||||
|
}).join("\n")
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window.customElements.define('d4s-ccp-input-simple', CCPSimpleInputWidgetController);
|
||||||
|
|
||||||
|
class CCPEnumInputWidgetController extends CCPBaseInputWidgetController{
|
||||||
|
|
||||||
|
constructor(){
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback(){
|
||||||
|
this.rootdoc.addEventListener("change", ev=>{
|
||||||
|
if(ev.target.getAttribute("name") === this.name){
|
||||||
|
const index = Number(ev.target.getAttribute("data-index"))
|
||||||
|
this.value[index] = ev.target.value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
content(){
|
||||||
|
const opts = this.options.map(o=>`<option value="${o}">${o}</option>`).join("\n")
|
||||||
|
if(this.value.length <= 1){
|
||||||
|
return `<select data-index="0" value="${this.value.length ? this.value[0] : ''}" class="my-2 form-control" placeholder="${this.title}" name="${this.name}" ${this.readonly ? 'disabled' : ''} ${this.required ? 'required' : ''}>${opts}</select>`
|
||||||
|
}
|
||||||
|
var out =
|
||||||
|
this.value.map((c,i)=>{
|
||||||
|
return `
|
||||||
|
<select data-index="${i}" value="${this.value.length ? this.value[0] : ''}" class="my-2 form-control" placeholder="${this.title}" name="${this.name}" ${this.readonly ? 'disabled' : ''} ${this.required ? 'required' : ''}>${opts}</select>
|
||||||
|
`
|
||||||
|
}).join("\n")
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window.customElements.define('d4s-ccp-input-enum', CCPEnumInputWidgetController);
|
||||||
|
|
||||||
|
class CCPChecklistInputWidgetController extends CCPBaseInputWidgetController{
|
||||||
|
|
||||||
|
constructor(){
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback(){
|
||||||
|
this.rootdoc.addEventListener("change", ev=>{
|
||||||
|
if(ev.target.getAttribute("name") === this.name){
|
||||||
|
const index = Number(ev.target.getAttribute("data-index"))
|
||||||
|
|
||||||
|
const elems = Array.prototype.slice.call(ev.currentTarget.querySelectorAll(`input[name='${this.name}'][data-index='${index}']`))
|
||||||
|
|
||||||
|
this.value[index] = elems.filter(e=>e.checked).map(e=>e.value).join(",")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
buildOpts(index, selections){
|
||||||
|
return this.options.map(o=>`
|
||||||
|
<div class="form-check form-switch form-check-inline">
|
||||||
|
<label>${o}</label>
|
||||||
|
<input data-index="${index}" class="form-check-input" type="checkbox" name="${this.name}" value="${o}" ${this.readonly ? 'readonly' : ''} ${selections.indexOf(o) > -1 ? 'checked' : ''}/></div>
|
||||||
|
`).join("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
content(){
|
||||||
|
if(this.value.length === 1){
|
||||||
|
const opts = this.buildOpts(0, this.value.length ? this.value[0].split(",") : [])
|
||||||
|
return `
|
||||||
|
<div class="my-2">
|
||||||
|
${opts}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
var out =
|
||||||
|
this.value.map((c,i)=>{
|
||||||
|
const opts = this.buildOpts(i, c.split(","))
|
||||||
|
return `
|
||||||
|
<div class="my-2">
|
||||||
|
${opts}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}).join("\n")
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window.customElements.define('d4s-ccp-input-checklist', CCPChecklistInputWidgetController);
|
||||||
|
|
||||||
|
class CCPTextAreaWidgetController extends CCPBaseInputWidgetController{
|
||||||
|
|
||||||
|
constructor(){
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback(){
|
||||||
|
this.rootdoc.addEventListener("input", ev=>{
|
||||||
|
if(ev.target.getAttribute("name") === this.name){
|
||||||
|
const index = Number(ev.target.getAttribute("data-index"))
|
||||||
|
this.value[index] = ev.target.value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
content(){
|
||||||
|
if(this.value.length <= 1){
|
||||||
|
return `<textarea data-index="0" class="my-2 form-control" placeholder="${this.title}" type="${this.type}" name="${this.name}" rows="5" ${this.readonly ? 'readonly' : ''} ${this.required ? 'required' : ''}>${this.value.length ? this.value[0] : ''}</textarea>`
|
||||||
|
}
|
||||||
|
var out =
|
||||||
|
this.value.map((c,i)=>{
|
||||||
|
return `
|
||||||
|
<textarea name="${this.name}" data-index="${i}" class="ccp-input my-2 form-control" placeholder="${this.title}" type="${this.type}" name="${this.name}" rows="5" ${this.readonly ? 'readonly' : ''} ${this.required ? 'required' : ''}>${c}</textarea>
|
||||||
|
`
|
||||||
|
}).join("\n")
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window.customElements.define('d4s-ccp-input-textarea', CCPTextAreaWidgetController);
|
||||||
|
|
||||||
|
class CCPFileInputWidgetController extends CCPBaseInputWidgetController{
|
||||||
|
|
||||||
|
constructor(){
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback(){
|
||||||
|
this.rootdoc.addEventListener("change", ev=>{
|
||||||
const tgt = ev.target
|
const tgt = ev.target
|
||||||
const ename = tgt.getAttribute("name")
|
if(tgt.getAttribute("name") === this.name){
|
||||||
if(ename === this.name){
|
const index = Number(tgt.getAttribute("data-index"))
|
||||||
const file = ev.target.files[0]
|
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){
|
if(file.size > 100*1024){
|
||||||
alert("This input allows only small files (100K). Use references instead ")
|
alert("This input allows only small files (100K). Use references instead ")
|
||||||
ev.stopPropagation()
|
ev.stopPropagation()
|
||||||
|
@ -189,197 +390,77 @@ class FileInputRenderer extends Renderer{
|
||||||
if ((encoded.length % 4) > 0) {
|
if ((encoded.length % 4) > 0) {
|
||||||
encoded += '='.repeat(4 - (encoded.length % 4));
|
encoded += '='.repeat(4 - (encoded.length % 4));
|
||||||
}
|
}
|
||||||
this.#content = encoded
|
this.value[index] = encoded
|
||||||
|
this.querySelector("small[name=preview]").textContent = encoded.substr(0,5) + "..." + encoded.substr(encoded.length-5)
|
||||||
})
|
})
|
||||||
reader.readAsDataURL(file)
|
reader.readAsDataURL(file)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
getValue(parent){
|
content(){
|
||||||
return this.#content
|
if(this.value.length <= 1){
|
||||||
}
|
const v = this.value.length ? this.value[0] : ''
|
||||||
|
return `
|
||||||
setValue(parent, v){
|
<input type="file" data-index="0" class="my-2 form-control" placeholder="${this.title}" name="${this.name}" ${this.readonly ? 'readonly' : ''} ${this.required && !v ? 'required' : ''} value="${v}"/>
|
||||||
parent.querySelector("input").value = v
|
<small name ="preview" class="form-text text-muted">${v.substr(0,5) + "..." + v.substr(v.length-5)}</small>
|
||||||
}
|
|
||||||
|
|
||||||
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
|
}
|
||||||
|
var out =
|
||||||
|
this.value.map((c,i)=>{
|
||||||
|
return `
|
||||||
|
<input type="file" data-index="${i}" class="my-2 form-control" placeholder="${this.title}" name="${this.name}" ${this.readonly ? 'readonly' : ''} ${this.required ? 'required' : ''} value="${c}"/>
|
||||||
|
<small name ="preview" class="form-text text-muted">${c.substr(0,5) + "..." + c.substr(s.length-5)}</small>
|
||||||
|
`
|
||||||
|
}).join("\n")
|
||||||
|
return out
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
window.customElements.define('d4s-ccp-input-file', CCPFileInputWidgetController);
|
||||||
|
|
||||||
class SecretInputRenderer extends Renderer{
|
class CCPSecretInputWidgetController extends CCPBaseInputWidgetController{
|
||||||
|
|
||||||
#html = null;
|
constructor(){
|
||||||
|
super()
|
||||||
constructor(input){
|
|
||||||
super(input)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getValue(parent){
|
connectedCallback(){
|
||||||
return parent.querySelector("input").value
|
this.rootdoc.addEventListener("input", ev=>{
|
||||||
|
if(ev.target.getAttribute("name") === this.name){
|
||||||
|
const index = Number(ev.target.getAttribute("data-index"))
|
||||||
|
this.value[index] = ev.target.value
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
setValue(parent, v){
|
this.rootdoc.addEventListener("click", ev=>{
|
||||||
parent.querySelector("input").value = v
|
if(ev.target.getAttribute("name") === "password_toggle"){
|
||||||
}
|
const index = Number(ev.target.getAttribute("data-index"))
|
||||||
|
const field = this.rootdoc.querySelector(`input[data-index='${index}']`)
|
||||||
connectedCallback(controller){
|
if(field.type === "text") field.type="password";
|
||||||
controller.addEventListener("click", ev=>{
|
else field.type = "text";
|
||||||
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(){
|
content(){
|
||||||
let required = this.required ? 'required="required"' : ""
|
if(this.value.length <= 1){
|
||||||
let readonly = this.readOnly ? 'readonly="readOnly"' : ""
|
return `
|
||||||
this.#html = `
|
<div style="position:relative">
|
||||||
<div class="ccp-input-widget ccp-input-widget form-field">
|
<input data-index="0" value="${this.value.length ? this.value[0] : ''}" class="my-2 form-control" placeholder="${this.title}" type="password" name="${this.name}" ${this.readonly ? 'readonly' : ''} ${this.required ? 'required' : ''}/>
|
||||||
<label>
|
<span data-index="0" class="btn" style="position:absolute; right:0; bottom:0" name="password_toggle">👁</span>
|
||||||
${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>
|
</div>
|
||||||
`
|
`
|
||||||
return this.#html
|
|
||||||
}
|
}
|
||||||
}
|
var out =
|
||||||
|
this.value.map((c,i)=>{
|
||||||
class DateTimeInputRenderer extends Renderer{
|
return `
|
||||||
|
<div style="position:relative">
|
||||||
#html = null;
|
<input name="${this.name}" value="${c}" data-index="${i}" class="ccp-input my-2 form-control" placeholder="${this.title}" type="password" name="${this.name}" ${this.readonly ? 'readonly' : ''} ${this.required ? 'required' : ''}/>
|
||||||
|
<span data-index="${i}" class="btn" style="position:absolute; right:0; bottom:0" name="password_toggle">👁</span>
|
||||||
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>
|
</div>
|
||||||
`
|
`
|
||||||
return this.#html
|
}).join("\n")
|
||||||
}
|
return out
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
window.customElements.define('d4s-ccp-input-secret', CCPSecretInputWidgetController);
|
|
@ -65,42 +65,48 @@ class CCPInputWidgetEditorController extends HTMLElement{
|
||||||
this.innerHTML = `
|
this.innerHTML = `
|
||||||
<details ${ reopen ? 'open' : ''}>
|
<details ${ reopen ? 'open' : ''}>
|
||||||
<summary class="mb-3">
|
<summary class="mb-3">
|
||||||
<input class="form-control" style="width:auto;display:inline" required="required" name="id" value="${this.#input.id}" title="Id of input" ${ input.id === 'ccpimage' ? 'readonly' : ''}/>
|
<input class="form-control" style="width:auto;display:inline" required="required" name="id" value="${input.id}" title="Id of input" ${ input.id === 'ccpimage' ? 'readonly' : ''}/>
|
||||||
${ input.id !== 'ccpimage' ? this.renderDeleteButton() : ''}
|
${ input.id !== 'ccpimage' ? this.renderDeleteButton() : ''}
|
||||||
</summary>
|
</summary>
|
||||||
<div style="padding-left: 1rem;border-left: 1px solid gray;">
|
<div style="padding-left: 1rem;border-left: 1px solid gray;">
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="form-field" title="Title">
|
<div class="form-field" title="Title">
|
||||||
<input name="title" class="form-control" placeholder="title" value="${this.#input.title}" required="required" ${ input.id === 'ccpimage' ? 'readonly' : ''}/>
|
<input name="title" class="form-control" placeholder="title" value="${input.title}" required="required" ${ input.id === 'ccpimage' ? 'readonly' : ''}/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<div class="form-field" title="description">
|
<div class="form-field" title="description">
|
||||||
<input name="description" class="form-control" placeholder="description" value="${this.#input.description}" required="required" ${ input.id === 'ccpimage' ? 'readonly' : ''}/>
|
<input name="description" class="form-control" placeholder="description" value="${input.description}" required="required" ${ input.id === 'ccpimage' ? 'readonly' : ''}/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<div class="col-3">
|
<div class="col-3">
|
||||||
<div class="form-field" title="Type">
|
<div class="form-field" title="Type">
|
||||||
<label>Type</label>
|
<label>Type</label>
|
||||||
<select name="type" class="form-control" placeholder="type" value="${this.#input.schema.type}" ${ input.id === 'ccpimage' ? 'readonly' : ''}>
|
<select name="type" class="form-control" placeholder="type" value="${this.#type}" ${ input.id === 'ccpimage' ? 'readonly' : ''}>
|
||||||
<option value="string">String</option>
|
<option value="string" ${this.#type === "string" ? "selected" : ""}>String</option>
|
||||||
<option value="enum">Enum</option>
|
<option value="enum" ${this.#type === "enum" ? "selected" : ""}>Enum</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="form-field" title="Minimum">
|
<div class="form-field" title="Minimum occurrences">
|
||||||
<label>Min</label>
|
<label>Min occurs</label>
|
||||||
<input value="${this.#input.minOccurs}" type="number" min="0" step="1" name="minOccurs" value="${this.#input.minOccurs}" required="required" class="form-control" placeholder="minOccurs" ${ input.id === 'ccpimage' ? 'readonly' : ''}/>
|
<input value="${input.minOccurs}" type="number" min="0" step="1" name="minOccurs" value="${input.minOccurs}" required="required" class="form-control" placeholder="minOccurs" ${ input.id === 'ccpimage' ? 'readonly' : ''}/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="form-field" title="Maximum">
|
<div class="form-field" title="Maximum occurrences">
|
||||||
<label>Max</label>
|
<label>Max occurs</label>
|
||||||
<input value="${this.#input.maxOccurs}" type="number" min="0" step="1" name="maxOccurs" value="${this.#input.maxOccurs}" required="required" class="form-control" placeholder="maxOccurs" ${ input.id === 'ccpimage' ? 'readonly' : ''}/>
|
<input value="${input.maxOccurs}" type="number" min="0" step="1" name="maxOccurs" value="${input.maxOccurs}" required="required" class="form-control" placeholder="maxOccurs" ${ input.id === 'ccpimage' ? 'readonly' : ''}/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col" title="Read only">
|
||||||
|
<label>Read only</label>
|
||||||
|
<div class="form-field">
|
||||||
|
<input type="checkbox" ${input.schema.readOnly ? 'checked' : ''} name="readonly" class="form-check-input">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -108,7 +114,7 @@ class CCPInputWidgetEditorController extends HTMLElement{
|
||||||
<div class="col-3">
|
<div class="col-3">
|
||||||
<div class="form-field" title="Format">
|
<div class="form-field" title="Format">
|
||||||
<label>Format</label>
|
<label>Format</label>
|
||||||
<select value="${this.#input.schema.format}" name="format" class="form-control" ${ input.id === 'ccpimage' ? 'readonly' : ''}>
|
<select value="${input.schema.format}" name="format" class="form-control" ${ input.id === 'ccpimage' ? 'readonly' : ''}>
|
||||||
<option value="none" ${this.isSelectedFormat('none') ? "selected" : ""}>None</option>
|
<option value="none" ${this.isSelectedFormat('none') ? "selected" : ""}>None</option>
|
||||||
<option value="date" ${this.isSelectedFormat('date') ? "selected" : ""}>Date</option>
|
<option value="date" ${this.isSelectedFormat('date') ? "selected" : ""}>Date</option>
|
||||||
<option value="time" ${this.isSelectedFormat('time') ? "selected" : ""}>Time</option>
|
<option value="time" ${this.isSelectedFormat('time') ? "selected" : ""}>Time</option>
|
||||||
|
@ -116,27 +122,29 @@ class CCPInputWidgetEditorController extends HTMLElement{
|
||||||
<option value="code" ${this.isSelectedFormat('code') ? "selected" : ""}>Code</option>
|
<option value="code" ${this.isSelectedFormat('code') ? "selected" : ""}>Code</option>
|
||||||
<option value="file" ${this.isSelectedFormat('file') ? "selected" : ""}>File</option>
|
<option value="file" ${this.isSelectedFormat('file') ? "selected" : ""}>File</option>
|
||||||
<option value="secret" ${this.isSelectedFormat('secret') ? "selected" : ""}>Secret</option>
|
<option value="secret" ${this.isSelectedFormat('secret') ? "selected" : ""}>Secret</option>
|
||||||
|
<option value="url" ${this.isSelectedFormat('url') ? "selected" : ""}>Url</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col" title="Mime type">
|
<div class="col" title="Mime type">
|
||||||
<label>Mime</label>
|
<label>Mime</label>
|
||||||
<div class="form-field">
|
<div class="form-field">
|
||||||
<input value="${this.#input.schema.contentMediaType}" name="contentMediaType" class="form-control" placeholder="mime" ${ input.id === 'ccpimage' ? 'readonly' : ''}/>
|
<input value="${input.schema.contentMediaType}" name="contentMediaType" class="form-control" placeholder="mime" ${ input.id === 'ccpimage' ? 'readonly' : ''}/>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col" title="Read only">
|
|
||||||
<label>Read only</label>
|
|
||||||
<div class="form-field">
|
|
||||||
<input type="checkbox" ${this.#input.schema.readOnly ? 'checked' : ''} name="readonly" class="form-check-input">
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div name="enum-input" class="${this.#type !== 'enum' ? 'd-none' : ''} mb-3">
|
<div name="enum-input" class="${this.#type !== 'enum' ? 'd-none' : ''} mb-3">
|
||||||
<div class="form-field" title="options">
|
<div class="form-field" title="options">
|
||||||
<input name="options" class="form-control" type="text" placeholder="option" value="${this.#input.schema.enum ? this.#input.schema.enum.join(',') : ''}"/>
|
<input name="options" class="form-control" type="text" placeholder="option" value="${input.schema.enum ? input.schema.enum.join(',') : ''}"/>
|
||||||
<small class="form-text text-muted">Comma separated list of options</small>
|
<small class="form-text text-muted">Comma separated list of options</small>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-field" title="Format">
|
||||||
|
<label>Format</label>
|
||||||
|
<select value="${input.schema.format}" name="format" class="form-control">
|
||||||
|
<option value="select" ${this.isSelectedFormat('select') ? "selected" : ""}>Choice</option>
|
||||||
|
<option value="checklist" ${this.isSelectedFormat('checklist') ? "selected" : ""}>Multi Choice</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div name="input-default" class="mb-3">
|
<div name="input-default" class="mb-3">
|
||||||
<div class="form-field" title="Default value">
|
<div class="form-field" title="Default value">
|
||||||
|
@ -176,7 +184,7 @@ class CCPInputWidgetEditorController extends HTMLElement{
|
||||||
}
|
}
|
||||||
else if(ename === "format"){
|
else if(ename === "format"){
|
||||||
this.#input.schema.format = val
|
this.#input.schema.format = val
|
||||||
this.render(this.#input, this.#index, true)
|
//this.render(this.#input, this.#index, true)
|
||||||
/*this.querySelector("div[name=input-default] span[name=password_toggle]").classList.add("d-none")
|
/*this.querySelector("div[name=input-default] span[name=password_toggle]").classList.add("d-none")
|
||||||
this.querySelector("div[name=input-default] input[name=default]").type = ""
|
this.querySelector("div[name=input-default] input[name=default]").type = ""
|
||||||
if(this.#input.schema.format === "secret"){
|
if(this.#input.schema.format === "secret"){
|
||||||
|
|
Loading…
Reference in New Issue