globular-mvc
Version:
Generic template to create web-application that made use of globular as backend and materialize as css (wrap in web-component's)
1,298 lines (1,067 loc) • 58.6 kB
JavaScript
import { v4 as uuidv4 } from "uuid";
import { Account } from "../Account";
import { Application } from "../Application";
import { getAllGroups, getAllRoles } from "globular-web-client/api";
import { Model } from "../Model";
import { getAllOrganizations } from "./Organization";
import { getAllPeers } from "./Peers";
import { fireResize } from "./utility";
import * as getUuidByString from "uuid-by-string";
export class EditableStringList extends HTMLElement {
// attributes.
// Create the applicaiton view.
constructor(list) {
super()
// Set the shadow dom.
this.attachShadow({ mode: 'open' });
// Innitialisation of the layout.
this.shadowRoot.innerHTML = `
<style>
.string-list{
display: flex;
flex-wrap: wrap;
min-height: 25px;
}
.string-list div{
align-items: center;
justify-content: center;
padding: 0px 4px 0px 4px;
margin-right: 5px;
margin-top: 5px;
border: 1px solid var(--palette-action-disabled);
}
iron-icon {
width: 16px;
height: 16px;
margin-left: 2px;
}
paper-input {
display: none;
}
iron-icon:hover {
cursor: pointer;
}
</style>
<div style="position: relative;">
<paper-icon-button id="add-item-btn" icon="icons:add" style="position: absolute; left: -40px;"></paper-icon-button>
<div class="string-list">
</div>
</div>
`
// give the focus to the input.
let stringListDiv = this.shadowRoot.querySelector(".string-list")
let range = document.createRange()
stringListDiv.onclick = () => {
this.blur()
}
this.shadowRoot.querySelector("#add-item-btn").onclick = () => {
this.addItem("New value", stringListDiv, range, true)
}
list.forEach(item => {
this.addItem(item, stringListDiv, range)
})
}
// Add item to the list.
addItem(item, stringListDiv, range, edit) {
let uuid = "_" + getUuidByString(item)
let itemDiv = stringListDiv.querySelector(`#${uuid}`)
if (itemDiv) {
itemDiv.children[0].click()
return
}
let html = `
<div id=${uuid} style="display: flex;">
<span class="items">${item}</span>
<paper-input no-label-float style="display: none;"></paper-input>
<iron-icon id="remove-btn" icon="icons:close"></iron-icon>
</div>
`
let index = stringListDiv.children.length
stringListDiv.appendChild(range.createContextualFragment(html))
// I will set edit event...
itemDiv = stringListDiv.children[index]
let itemSpan = itemDiv.children[0]
let itemInput = itemDiv.children[1]
let removeBtn = itemDiv.children[2]
// Delete the item.
removeBtn.onclick = (evt) => {
evt.stopPropagation()
itemDiv.parentNode.removeChild(itemDiv)
}
itemSpan.onclick = (evt) => {
evt.stopPropagation()
for (var i = 0; i < stringListDiv.children.length; i++) {
stringListDiv.children[i].children[0].style.display = "block"
stringListDiv.children[i].children[1].style.display = "none"
}
itemInput.style.display = "block"
itemInput.value = itemSpan.innerHTML
itemSpan.style.display = "none"
setTimeout(() => {
itemInput.focus()
itemInput.inputElement.inputElement.select()
}, 100)
fireResize()
}
itemInput.onkeyup = (evt) => {
evt.stopPropagation()
let key = evt.key;
if (key == "Escape") {
itemSpan.innerHTML = itemInput.value = item // set back to item...
itemInput.style.display = "none"
itemSpan.style.display = "block"
}
if (key == "Enter") {
let uuid = "_" + getUuidByString(itemInput.value)
itemDiv.id = uuid
let count = 0;
for (var i = 0; i < stringListDiv.children.length; i++) {
if (stringListDiv.children[i].id == uuid) {
count++
}
}
if (count >= 1) {
let itemDiv_ = stringListDiv.querySelector("#" + uuid)
itemDiv_.children[1].value = itemInput.value
if (count > 1) {
itemDiv.parentNode.removeChild(itemDiv)
itemDiv_.children[0].click()
return
}
}
// save value
itemSpan.innerHTML = itemInput.value
itemInput.style.display = "none"
itemSpan.style.display = "block"
}
}
itemInput.onblur = () => {
// make sure there not repetead values...
let uuid = "_" + getUuidByString(itemInput.value)
itemDiv.id = uuid
itemSpan.innerHTML = itemInput.value
let count = 0;
for (var i = 0; i < stringListDiv.children.length; i++) {
if (stringListDiv.children[i].id == uuid) {
count++
}
}
if (count >= 1) {
let itemDiv_ = stringListDiv.querySelector("#" + uuid)
itemDiv_.children[1].value = itemInput.value
if (count > 1) {
itemDiv.parentNode.removeChild(itemDiv)
itemDiv_.children[0].click()
return
}
}
}
if (edit) {
itemSpan.click()
}
}
blur() {
let inputs = this.shadowRoot.querySelectorAll("paper-input")
for (var i = 0; i < inputs.length; i++) {
inputs[i].style.display = "none"
inputs[i].parentNode.children[0].style.display = "block"
}
}
getItems(){
let spans = this.shadowRoot.querySelectorAll(".items")
let items = []
for(var i=0; i < spans.length; i++){
items.push(spans[i].innerHTML)
}
return items;
}
}
customElements.define('globular-editable-string-list', EditableStringList)
/**
* String seach listbox.
*/
export class SearchableList extends HTMLElement {
// attributes.
// Create the applicaiton view.
constructor(title, list, ondeleteitem, onadditem, onadd) {
super()
// handler...
this.ondeleteitem = ondeleteitem;
this.onadditem = onadditem;
// Set the shadow dom.
this.attachShadow({ mode: 'open' });
this.list = list
this.title = title
// Innitialisation of the layout.
this.shadowRoot.innerHTML = `
<style>
.header{
position: relative;
transition: background 0.2s ease,padding 0.8s linear;
padding-left: 10px;
}
.item-div:hover{
-webkit-filter: invert(10%);
filter: invert(10%);
}
.item-div{
padding: 5px;
display: flex;
align-items: center;
font-size: 1.125rem;
}
.icon-button{
cursor: pointer;
}
::-webkit-scrollbar {
width: 5px;
height: 5px;
}
::-webkit-scrollbar-track {
background: var(--palette-background-default);
}
::-webkit-scrollbar-thumb {
background: var(--palette-divider);
}
</style>
<div id="header-div" class="header">
<span class="title">${title} (${list.length})</span>
<div style="display:flex; flex-direction: row; align-items: center;">
<div class="icon-button" style="display: flex; width: 24px; height: 24px; justify-content: center; align-items: center;position: relative;">
<iron-icon id="action-add-btn" icon="add" style="flex-grow: 1; --iron-icon-fill-color:var(--palette-text-primary);"></iron-icon>
<paper-ripple class="circle" recenters=""></paper-ripple>
</div>
<div style="flex-grow: 1;">
<paper-input style="padding-left: 15px; max-width: 300px;" type="text" label="Filter ${title}"></paper-input>
</div>
</div>
</div>
<div id="shadow-div" style="width: 100%; height: 5px;"></div>
<div id="items-div" style="width: 100%;max-height: 200px; overflow-y: auto; maring-top: 5px; margin-bottom: 5px;"></div>
`
this.listDiv = this.shadowRoot.querySelector("#items-div")
let shadowDiv = this.shadowRoot.querySelector("#shadow-div")
// set the header shadow...
this.listDiv.onscroll = () => {
if (this.listDiv.scrollTop == 0) {
shadowDiv.style.boxShadow = ""
} else {
shadowDiv.style.boxShadow = "inset 0px 5px 6px -3px rgb(0 0 0 / 40%)"
}
}
// Here I will display the filter input.
let filterInput = this.shadowRoot.querySelector("paper-input")
this.filter_ = ""
filterInput.onkeyup = () => {
this.filter_ = filterInput.value;
this.displayItems()
}
// Call the on add function
let addBtn = this.shadowRoot.querySelector("#action-add-btn")
if (onadd != undefined) {
addBtn.onclick = () => {
onadd(this.list)
}
}
// Here I will create the action list...
this.displayItems()
}
// Return the header div.
getHeader() {
return this.shadowRoot.querySelector("#header-div")
}
// That function can be overide, assume a string by default
filter(item) {
return item.toUpperCase().indexOf(this.filter_.toUpperCase()) != -1
}
// The sort items function
sortItems() {
return this.list.sort()
}
hideTitle() {
this.shadowRoot.querySelector(".title").style.display = "none";
}
// The function that display one item.
displayItem(item) {
let uuid = "_" + uuidv4();
let html = `
<div id="${uuid}" class="item-div" style="">
<div style="flex-grow: 1; line-break: anywhere;">${item}</div>
<paper-icon-button icon="delete"></paper-icon-button>
</div>`
let div = document.createRange().createContextualFragment(html)
let deleteBtn = div.querySelector("paper-icon-button")
if (this.ondeleteitem != undefined) {
deleteBtn.onclick = () => {
// remove the div...
let div = this.shadowRoot.querySelector("#" + uuid)
div.parentNode.removeChild(div)
this.ondeleteitem(item)
}
} else {
deleteBtn.style.display = "none"
}
return div
}
// That function display items.
displayItems() {
// clean up the list of items.
this.listDiv.innerHTML = ""
this.sortItems().forEach((item) => {
let div = this.displayItem(item)
if (this.filter(item) || this.filter_.length == 0) {
this.listDiv.appendChild(div)
}
})
}
removeItem(str) {
this.list = this.list.filter(el => el !== str)
let titleDiv = this.shadowRoot.querySelector(".title")
titleDiv.innerHTML = `${this.title} (${this.list.length})`
this.displayItems()
}
appendItem(item) {
this.list.push(item)
let titleDiv = this.shadowRoot.querySelector(".title")
titleDiv.innerHTML = `${this.title} (${this.list.length})`
this.displayItems()
}
}
customElements.define('globular-searchable-list', SearchableList)
/**
* Searchable Account list
*/
export class SearchableAccountList extends SearchableList {
// attributes.
// Create the applicaiton view.
constructor(title, list, ondeleteaccount, onaddaccount) {
// the onadd handler
let onadd = (accounts) => {
// Now the user list...
Account.getAccounts("{}", (allAccounts) => {
accounts.forEach(a => {
// remove all existing items.
allAccounts = allAccounts.filter(el => el._id !== a._id)
})
// Now I will display the list of available account to add to the role...
let html = `
<style>
#add-list-user-panel{
position: absolute;
left: 0px;
z-index: 1;
background-color: var(--palette-background-paper);
}
.card-content{
overflow-y: auto;
min-width: 400px;
}
paper-card{
background-color: var(--palette-background-paper);
color: var(--palette-text-primary);
}
</style>
<paper-card id="add-list-user-panel">
<div style="display: flex; align-items: center;">
<div style="flex-grow: 1; padding: 5px;">
Add Account
</div>
<paper-icon-button id="cancel-btn" icon="close"></paper-icon-button>
</div>
<div class="card-content">
<globular-autocomplete type="email" label="Search Account" id="add_account_input" width="${this.width - 10}" style="flex-grow: 1;"></globular-autocomplete>
</div>
</paper-card>
`
let headerDiv = this.shadowRoot.querySelector("#header-div")
let panel = headerDiv.querySelector("#add-list-user-panel")
if (panel == undefined) {
headerDiv.appendChild(document.createRange().createContextualFragment(html))
panel = headerDiv.querySelector("#add-list-user-panel")
panel.style.top = (headerDiv.offsetHeight / 2) + 14 + "px";
let closeBtn = panel.querySelector("#cancel-btn")
closeBtn.onclick = () => {
panel.parentNode.removeChild(panel)
}
// The invite contact action.
let addAccountInput = this.shadowRoot.getElementById("add_account_input")
addAccountInput.focus()
addAccountInput.onkeyup = () => {
let val = addAccountInput.getValue();
if (val.length >= 2) {
let values = []
allAccounts.forEach(a => {
if (a.name.toUpperCase().indexOf(val.toUpperCase()) != -1 || a.email_.toUpperCase().indexOf(val.toUpperCase()) != -1) {
values.push(a)
}
})
addAccountInput.setValues(values)
} else {
addAccountInput.clear()
}
}
// That function must return the div that display the value that we want.
addAccountInput.displayValue = (a) => {
// display the account...
let div = this.createAccountDiv(a)
div.children[0].style.width = "auto"
let addBtn = div.querySelector("paper-icon-button")
addBtn.icon = "add"
addBtn.onclick = () => {
// remove the account form the list off available choice.
allAccounts = allAccounts.filter(a_ => a_ !== a)
addAccountInput.clear()
// set values without the account
let values = []
let val = addAccountInput.getValue();
allAccounts.forEach(a => {
if (a.name.toUpperCase().indexOf(val.toUpperCase()) != -1 || a.email_.toUpperCase().indexOf(val.toUpperCase()) != -1) {
values.push(a)
}
})
addAccountInput.setValues(values)
// Here I will set the account role...
if (this.onadditem != null) {
this.onadditem(a)
}
}
return div
}
}
})
}
super(title, list, ondeleteaccount, onaddaccount, onadd)
}
createAccountDiv(account) {
let uuid = "_" + uuidv4();
let html = `
<style>
</style>
<div id="${uuid}" class="item-div" style="">
<div style="display: flex; align-items: center; padding: 5px; width: 100%;">
<img style="width: 40px; height: 40px; display: ${account.profilePicture.length == 0 ? "none" : "block"};" src="${account.profilePicture}"></img>
<iron-icon icon="account-circle" style="width: 40px; height: 40px; --iron-icon-fill-color:var(--palette-action-disabled); display: ${account.profilePicture.length != 0 ? "none" : "block"};"></iron-icon>
<div style="display: flex; flex-direction: column; width:200px; font-size: .85em; padding-left: 8px; flex-grow: 1;">
<span>${account.name}</span>
<span>${account.email_}</span>
</div>
<paper-icon-button icon="delete" id="${account._id}_btn"></paper-icon-button>
</div>
</div>`
this.shadowRoot.appendChild(document.createRange().createContextualFragment(html))
let div = this.shadowRoot.getElementById(uuid)
div.parentNode.removeChild(div)
return div
}
// Remove an accout from the list.
removeItem(a) {
this.list = this.list.filter(el => el._id !== a._id)
}
displayItem(a) {
let div = this.createAccountDiv(a)
let deleteBtn = div.querySelector("paper-icon-button")
deleteBtn.icon = "delete"
if (this.ondeleteitem != undefined) {
deleteBtn.onclick = () => {
// remove the div...
div.parentNode.removeChild(div)
this.ondeleteitem(a)
}
} else {
deleteBtn.style.display = "none"
}
return div
}
// That function can be overide, assume a string by default
filter(account) {
return account.name.toUpperCase().indexOf(this.filter_.toUpperCase()) != -1 || account.email_.toUpperCase().indexOf(this.filter_.toUpperCase()) != -1
}
// The sort items function
sortItems() {
// Sort account...
return this.list.sort((a, b) => (a.name > b.name) ? 1 : -1)
}
}
customElements.define('globular-searchable-account-list', SearchableAccountList)
/**
* Searchable Application list
*/
export class SearchableApplicationList extends SearchableList {
// attributes.
// Create the applicaiton view.
constructor(title, list, ondeleteapplication, onaddapplication) {
// the onadd handler
let onadd = (applications) => {
// Now the user list...
Application.getAllApplicationInfo((allApplications) => {
applications.forEach(a => {
// remove all existing items.
allApplications = allApplications.filter(el => el._id !== a._id)
})
// Now I will display the list of available account to add to the role...
let html = `
<style>
#add-list-application-panel{
position: absolute;
left: 0px;
z-index: 1;
background-color: var(--palette-background-paper);
}
.card-content{
overflow-y: auto;
min-width: 400px;
}
paper-card{
background-color: var(--palette-background-paper);
color: var(--palette-text-primary);
}
</style>
<paper-card id="add-list-application-panel">
<div style="display: flex; align-items: center;">
<div style="flex-grow: 1; padding: 5px;">
Add Application
</div>
<paper-icon-button id="cancel-btn" icon="close"></paper-icon-button>
</div>
<div class="card-content">
<globular-autocomplete type="text" label="Search Application" id="add_application_input" width="${this.width - 10}" style="flex-grow: 1;"></globular-autocomplete>
</div>
</paper-card>
`
let headerDiv = this.shadowRoot.querySelector("#header-div")
let panel = headerDiv.querySelector("#add-list-application-panel")
if (panel == undefined) {
headerDiv.appendChild(document.createRange().createContextualFragment(html))
panel = headerDiv.querySelector("#add-list-application-panel")
panel.style.top = (headerDiv.offsetHeight / 2) + 14 + "px";
let closeBtn = panel.querySelector("#cancel-btn")
closeBtn.onclick = () => {
panel.parentNode.removeChild(panel)
}
// The invite contact action.
let addApplicationInput = this.shadowRoot.getElementById("add_application_input")
addApplicationInput.focus()
addApplicationInput.onkeyup = () => {
let val = addApplicationInput.getValue();
if (val.length >= 2) {
let values = []
allApplications.forEach(a => {
if (a.getName().toUpperCase().indexOf(val.toUpperCase()) != -1 || a.getAlias().toUpperCase().indexOf(val.toUpperCase()) != -1) {
values.push(a)
}
})
addApplicationInput.setValues(values)
} else {
addApplicationInput.clear()
}
}
// That function must return the div that display the value that we want.
addApplicationInput.displayValue = (a) => {
// display the account...
let div = this.createApplicationDiv(a)
div.children[0].style.width = "auto"
let addBtn = div.querySelector("paper-icon-button")
addBtn.icon = "add"
addBtn.onclick = () => {
// remove the account form the list off available choice.
allApplications = allApplications.filter(a_ => a_ !== a)
addApplicationInput.clear()
// set values without the account
let values = []
let val = addApplicationInput.getValue();
allApplications.forEach(a => {
if (a.name.toUpperCase().indexOf(val.toUpperCase()) != -1) {
values.push(a)
}
})
addApplicationInput.setValues(values)
// Here I will set the account role...
if (this.onadditem != null) {
this.onadditem(a)
}
}
return div
}
}
})
}
super(title, list, ondeleteapplication, onaddapplication, onadd)
}
createApplicationDiv(application) {
let uuid = "_" + uuidv4();
let html = `
<style>
</style>
<div id="${uuid}" class="item-div" style="">
<div style="display: flex; align-items: center; padding: 5px; width: 100%;">
<img style="width: 40px; height: 40px; display: ${application.getIcon() == undefined ? "none" : "block"};" src="${application.getIcon()}"></img>
<iron-icon icon="account-circle" style="width: 40px; height: 40px; --iron-icon-fill-color:var(--palette-action-disabled); display: ${application.getIcon() != undefined ? "none" : "block"};"></iron-icon>
<div style="display: flex; flex-direction: column; width:200px; font-size: .85em; padding-left: 8px; flex-grow: 1;">
<span>${application.getAlias()}</span>
<span>${application.getVersion()}</span>
</div>
<paper-icon-button icon="delete" id="${application._id}_btn"></paper-icon-button>
</div>
</div>`
this.shadowRoot.appendChild(document.createRange().createContextualFragment(html))
let div = this.shadowRoot.getElementById(uuid)
div.parentNode.removeChild(div)
return div
}
// Remove an accout from the list.
removeItem(a) {
this.list = this.list.filter(el => el._id !== a._id)
}
displayItem(a) {
let div = this.createApplicationDiv(a)
let deleteBtn = div.querySelector("paper-icon-button")
deleteBtn.icon = "delete"
if (this.ondeleteitem != undefined) {
deleteBtn.onclick = () => {
// remove the div...
div.parentNode.removeChild(div)
this.ondeleteitem(a)
}
} else {
deleteBtn.style.display = "none"
}
return div
}
// That function can be overide, assume a string by default
filter(a) {
return a.getName().toUpperCase().indexOf(this.filter_.toUpperCase()) != -1 || a.getAlias().toUpperCase().indexOf(this.filter_.toUpperCase()) != -1
}
// The sort items function
sortItems() {
// Sort account...
return this.list.sort((a, b) => (a.getName() > b.getName()) ? 1 : -1)
}
}
customElements.define('globular-searchable-application-list', SearchableApplicationList)
/**
* Searchable Role list
*/
export class SearchableRoleList extends SearchableList {
// attributes.
// Create the applicaiton view.
constructor(title, list, ondeleterole, onaddrole) {
// the onadd handler
let onadd = (roles) => {
// Now the user list...
getAllRoles(Model.globular, (allRoles) => {
roles.forEach(r => {
// remove all existing items.
allRoles = allRoles.filter(el => el.getId() !== r.getId())
})
// Now I will display the list of available account to add to the role...
let html = `
<style>
#add-list-role-panel{
position: absolute;
left: 0px;
z-index: 1;
background-color: var(--palette-background-paper);
}
.card-content{
overflow-y: auto;
min-width: 400px;
}
paper-card{
background-color: var(--palette-background-paper);
color: var(--palette-text-primary);
}
</style>
<paper-card id="add-list-role-panel">
<div style="display: flex; align-items: center;">
<div style="flex-grow: 1; padding: 5px;">
Add Role
</div>
<paper-icon-button id="cancel-btn" icon="close"></paper-icon-button>
</div>
<div class="card-content">
<globular-autocomplete type="text" label="Search Role" id="add_role_input" width="${this.width - 10}" style="flex-grow: 1;"></globular-autocomplete>
</div>
</paper-card>
`
let headerDiv = this.shadowRoot.querySelector("#header-div")
let panel = headerDiv.querySelector("#add-list-role-panel")
if (panel == undefined) {
headerDiv.appendChild(document.createRange().createContextualFragment(html))
panel = headerDiv.querySelector("#add-list-role-panel")
panel.style.top = (headerDiv.offsetHeight / 2) + 14 + "px";
let closeBtn = panel.querySelector("#cancel-btn")
closeBtn.onclick = () => {
panel.parentNode.removeChild(panel)
}
// The invite contact action.
let addRoleInput = this.shadowRoot.getElementById("add_role_input")
addRoleInput.focus()
addRoleInput.onkeyup = () => {
let val = addRoleInput.getValue();
if (val.length >= 2) {
let values = []
allRoles.forEach(r => {
if (r.getName().toUpperCase().indexOf(val.toUpperCase()) != -1 || r.getId().toUpperCase().indexOf(val.toUpperCase()) != -1) {
values.push(r)
}
})
addRoleInput.setValues(values)
} else {
addRoleInput.clear()
}
}
// That function must return the div that display the value that we want.
addRoleInput.displayValue = (r) => {
// display the account...
let div = this.createRoleDiv(r)
div.children[0].style.width = "auto"
let addBtn = div.querySelector("paper-icon-button")
addBtn.icon = "add"
addBtn.onclick = () => {
// remove the account form the list off available choice.
allRoles = allRoles.filter(r_ => r_ !== r)
addRoleInput.clear()
// set values without the account
let values = []
let val = addRoleInput.getValue();
allRoles.forEach(r => {
if (r.getName().toUpperCase().indexOf(val.toUpperCase()) != -1) {
values.push(r)
}
})
addRoleInput.setValues(values)
// Here I will set the account role...
if (this.onadditem != null) {
this.onadditem(r)
}
}
return div
}
}
})
}
super(title, list, ondeleterole, onaddrole, onadd)
}
// The div that display the role.
createRoleDiv(role) {
let uuid = "_" + uuidv4();
let html = `
<style>
</style>
<div id="${uuid}" class="item-div" style="">
<div style="display: flex; align-items: center; padding: 5px; width: 100%;">
<iron-icon icon="notification:enhanced-encryption" style="width: 40px; height: 40px; --iron-icon-fill-color:var(--palette-action-disabled); display:block"};"></iron-icon>
<div style="display: flex; flex-direction: column; width:200px; font-size: .85em; padding-left: 8px; flex-grow: 1;">
<span>${role.getName()}</span>
</div>
<paper-icon-button icon="delete" id="${role.getId()}_btn"></paper-icon-button>
</div>
</div>`
this.shadowRoot.appendChild(document.createRange().createContextualFragment(html))
let div = this.shadowRoot.getElementById(uuid)
div.parentNode.removeChild(div)
return div
}
// Remove an accout from the list.
removeItem(r) {
this.list = this.list.filter(el => el.getId() !== r.getId())
}
displayItem(a) {
let div = this.createRoleDiv(a)
let deleteBtn = div.querySelector("paper-icon-button")
deleteBtn.icon = "delete"
if (this.ondeleteitem != undefined) {
deleteBtn.onclick = () => {
// remove the div...
div.parentNode.removeChild(div)
this.ondeleteitem(a)
}
} else {
deleteBtn.style.display = "none"
}
return div
}
// That function can be overide, assume a string by default
filter(r) {
return r.getName().toUpperCase().indexOf(this.filter_.toUpperCase()) != -1 || r.getName().toUpperCase().indexOf(this.filter_.toUpperCase()) != -1
}
// The sort items function
sortItems() {
// Sort account...
return this.list.sort((a, b) => (a.getName() > b.getName()) ? 1 : -1)
}
}
customElements.define('globular-searchable-role-list', SearchableRoleList)
/**
* Searchable Group list
*/
export class SearchableGroupList extends SearchableList {
// attributes.
// Create the applicaiton view.
constructor(title, list, ondeletegroup, onaddgroup) {
// the onadd handler
let onadd = (groups) => {
// Now the user list...
getAllGroups(Model.globular, (allGroups) => {
groups.forEach(g => {
// remove all existing items.
allGroups = allGroups.filter(el => el.getId() !== g.getId())
})
let html = `
<style>
#add-list-group-panel{
position: absolute;
left: 0px;
z-index: 1;
background-color: var(--palette-background-paper);
}
.card-content{
overflow-y: auto;
min-width: 400px;
}
paper-card{
background-color: var(--palette-background-paper);
color: var(--palette-text-primary);
}
</style>
<paper-card id="add-list-group-panel">
<div style="display: flex; align-items: center;">
<div style="flex-grow: 1; padding: 5px;">
Add Group
</div>
<paper-icon-button id="cancel-btn" icon="close"></paper-icon-button>
</div>
<div class="card-content">
<globular-autocomplete type="text" label="Search Group" id="add_group_input" width="${this.width - 10}" style="flex-grow: 1;"></globular-autocomplete>
</div>
</paper-card>
`
let headerDiv = this.shadowRoot.querySelector("#header-div")
let panel = headerDiv.querySelector("#add-list-group-panel")
if (panel == undefined) {
headerDiv.appendChild(document.createRange().createContextualFragment(html))
panel = headerDiv.querySelector("#add-list-group-panel")
panel.style.top = (headerDiv.offsetHeight / 2) + 14 + "px";
let closeBtn = panel.querySelector("#cancel-btn")
closeBtn.onclick = () => {
panel.parentNode.removeChild(panel)
}
// The invite contact action.
let addGroupInput = this.shadowRoot.getElementById("add_group_input")
addGroupInput.focus()
addGroupInput.onkeyup = () => {
let val = addGroupInput.getValue();
if (val.length >= 2) {
let values = []
allGroups.forEach(g => {
if (g.getName().toUpperCase().indexOf(val.toUpperCase()) != -1 || g.getId().toUpperCase().indexOf(val.toUpperCase()) != -1) {
values.push(g)
}
})
addGroupInput.setValues(values)
} else {
addGroupInput.clear()
}
}
// That function must return the div that display the value that we want.
addGroupInput.displayValue = (g) => {
// display the account...
let div = this.createGroupDiv(g)
div.children[0].style.width = "auto"
let addBtn = div.querySelector("paper-icon-button")
addBtn.icon = "add"
addBtn.onclick = () => {
// remove the account form the list off available choice.
allGroups = allGroups.filter(g_ => g_ !== g)
addGroupInput.clear()
// set values without the account
let values = []
let val = addGroupInput.getValue();
allGroups.forEach(g => {
if (g.getName().toUpperCase().indexOf(val.toUpperCase()) != -1) {
values.push(g)
}
})
addGroupInput.setValues(values)
// Here I will set the account role...
if (this.onadditem != null) {
this.onadditem(g)
}
}
return div
}
}
})
}
super(title, list, ondeletegroup, onaddgroup, onadd)
}
// The div that display the role.
createGroupDiv(group) {
let uuid = "_" + uuidv4();
let html = `
<style>
</style>
<div id="${uuid}" class="item-div" style="">
<div style="display: flex; align-items: center; padding: 5px; width: 100%;">
<iron-icon icon="social:people" style="width: 40px; height: 40px; --iron-icon-fill-color:var(--palette-action-disabled); display:block"};"></iron-icon>
<div style="display: flex; flex-direction: column; width:200px; font-size: .85em; padding-left: 8px; flex-grow: 1;">
<span>${group.getName() + "@" + group.getDomain()}</span>
</div>
<paper-icon-button icon="delete" id="${group.getId()}_btn"></paper-icon-button>
</div>
</div>`
this.shadowRoot.appendChild(document.createRange().createContextualFragment(html))
let div = this.shadowRoot.getElementById(uuid)
div.parentNode.removeChild(div)
return div
}
// Remove an accout from the list.
removeItem(g) {
this.list = this.list.filter(el => el.getId() !== g.getId())
}
displayItem(g) {
let div = this.createGroupDiv(g)
let deleteBtn = div.querySelector("paper-icon-button")
deleteBtn.icon = "delete"
if (this.ondeleteitem != undefined) {
deleteBtn.onclick = () => {
// remove the div...
div.parentNode.removeChild(div)
this.ondeleteitem(g)
}
} else {
deleteBtn.style.display = "none"
}
return div
}
// That function can be overide, assume a string by default
filter(g) {
return g.getName().toUpperCase().indexOf(this.filter_.toUpperCase()) != -1 || g.getId().toUpperCase().indexOf(this.filter_.toUpperCase()) != -1
}
// The sort items function
sortItems() {
// Sort account...
return this.list.sort((a, b) => (a.getName() > b.getName()) ? 1 : -1)
}
}
customElements.define('globular-searchable-group-list', SearchableGroupList)
/**
* Searchable Organization list
*/
export class SearchableOrganizationList extends SearchableList {
// attributes.
// Create the applicaiton view.
constructor(title, list, ondeleteorganization, onaddorganization) {
// the onadd handler
let onadd = (organizations) => {
// Now the user list...
getAllOrganizations(allOrganizations => {
organizations.forEach(o => {
// remove all existing items.
allOrganizations = allOrganizations.filter(el => el.getId() !== o.getId())
})
let html = `
<style>
#add-list-organization-panel{
position: absolute;
left: 0px;
z-index: 1;
background-color: var(--palette-background-paper);
}
.card-content{
overflow-y: auto;
min-width: 400px;
}
paper-card{
background-color: var(--palette-background-paper);
color: var(--palette-text-primary);
}
</style>
<paper-card id="add-list-organization-panel">
<div style="display: flex; align-items: center;">
<div style="flex-grow: 1; padding: 5px;">
Add Organization
</div>
<paper-icon-button id="cancel-btn" icon="close"></paper-icon-button>
</div>
<div class="card-content">
<globular-autocomplete type="text" label="Search Group" id="add_organization_input" width="${this.width - 10}" style="flex-grow: 1;"></globular-autocomplete>
</div>
</paper-card>
`
let headerDiv = this.shadowRoot.querySelector("#header-div")
let panel = headerDiv.querySelector("#add-list-organization-panel")
if (panel == undefined) {
headerDiv.appendChild(document.createRange().createContextualFragment(html))
panel = headerDiv.querySelector("#add-list-organization-panel")
panel.style.top = (headerDiv.offsetHeight / 2) + 14 + "px";
let closeBtn = panel.querySelector("#cancel-btn")
closeBtn.onclick = () => {
panel.parentNode.removeChild(panel)
}
// The invite contact action.
let addOrganizationInput = this.shadowRoot.getElementById("add_organization_input")
addOrganizationInput.focus()
addOrganizationInput.onkeyup = () => {
let val = addOrganizationInput.getValue();
if (val.length >= 2) {
let values = []
allOrganizations.forEach(o => {
if (o.getName().toUpperCase().indexOf(val.toUpperCase()) != -1 || o.getId().toUpperCase().indexOf(val.toUpperCase()) != -1) {
values.push(o)
}
})
addOrganizationInput.setValues(values)
} else {
addOrganizationInput.clear()
}
}
// That function must return the div that display the value that we want.
addOrganizationInput.displayValue = (o) => {
// display the account...
let div = this.createOrganizationDiv(o)
div.children[0].style.width = "auto"
let addBtn = div.querySelector("paper-icon-button")
addBtn.icon = "add"
addBtn.onclick = () => {
// remove the account form the list off available choice.
allOrganizations = allOrganizations.filter(o_ => o_ !== o)
addOrganizationInput.clear()
// set values without the account
let values = []
let val = addOrganizationInput.getValue();
allOrganizations.forEach(o => {
if (o.getName().toUpperCase().indexOf(val.toUpperCase()) != -1) {
values.push(o)
}
})
addOrganizationInput.setValues(values)
// Here I will set the account role...
if (this.onadditem != null) {
this.onadditem(o)
}
}
return div
}
}
})
}
super(title, list, ondeleteorganization, onaddorganization, onadd)
}
// The div that display the role.
createOrganizationDiv(organization) {
let uuid = "_" + uuidv4();
let html = `
<style>
</style>
<div id="${uuid}" class="item-div" style="">
<div style="display: flex; align-items: center; padding: 5px; width: 100%;">
<iron-icon icon="social:domain" style="width: 40px; height: 40px; --iron-icon-fill-color:var(--palette-action-disabled); display:block"};"></iron-icon>
<div style="display: flex; flex-direction: column; width:200px; font-size: .85em; padding-left: 8px; flex-grow: 1;">
<span>${organization.getName()}</span>
</div>
<paper-icon-button icon="delete" id="${organization.getId()}_btn"></paper-icon-button>
</div>
</div>`
this.shadowRoot.appendChild(document.createRange().createContextualFragment(html))
let div = this.shadowRoot