@programmerg/bs-elements 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +9 -0
- package/README.md +148 -0
- package/custom-elements.json +7773 -0
- package/dist/bs-elements.bundle.min.js +8 -0
- package/dist/bs-elements.bundle.min.js.map +1 -0
- package/dist/bs-elements.esm.js +1721 -0
- package/dist/bs-elements.esm.js.map +1 -0
- package/dist/bs-elements.min.js +2 -0
- package/dist/bs-elements.min.js.map +1 -0
- package/package.json +50 -0
- package/src/base/BsAccordion.js +116 -0
- package/src/base/BsAlert.js +91 -0
- package/src/base/BsButton.js +129 -0
- package/src/base/BsCarousel.js +216 -0
- package/src/base/BsCollapse.js +84 -0
- package/src/base/BsDropdown.js +98 -0
- package/src/base/BsModal.js +204 -0
- package/src/base/BsOffcanvas.js +157 -0
- package/src/base/BsPopover.js +31 -0
- package/src/base/BsTab.js +69 -0
- package/src/base/BsToast.js +163 -0
- package/src/base/BsTooltip.js +118 -0
- package/src/core/BsElement.js +81 -0
- package/src/core/ReactiveElement.js +183 -0
- package/src/extra/BsCalendar.js +14 -0
- package/src/extra/BsCombobox.js +14 -0
- package/src/extra/BsForm.js +152 -0
- package/src/extra/BsInput.js +209 -0
- package/src/extra/BsTable.js +210 -0
- package/src/extra/BsTabs.js +0 -0
- package/src/extra/BsToastManager.js +0 -0
- package/src/extra/BsTree.js +14 -0
- package/src/extra/BsUploader.js +154 -0
- package/src/index.js +12 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import BsElement from "../core/BsElement.js";
|
|
2
|
+
|
|
3
|
+
let csrfToken = '';
|
|
4
|
+
export const setCsrfToken = (token) => {
|
|
5
|
+
csrfToken = token;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export async function httpRequest (opts) {
|
|
9
|
+
let url = opts.url,
|
|
10
|
+
method = opts.method ?? 'get',
|
|
11
|
+
contentType = '',
|
|
12
|
+
data = opts.data;
|
|
13
|
+
|
|
14
|
+
switch (opts.contentType) {
|
|
15
|
+
case 'text': contentType = 'text/plain'; break;
|
|
16
|
+
case 'multipart': contentType = 'multipart/form-data'; break;
|
|
17
|
+
case 'urlencoded': contentType = 'application/x-www-form-urlencoded'; break;
|
|
18
|
+
case 'json': default: contentType = 'application/json'; break;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Populate opts from FORM attributes
|
|
22
|
+
if (opts.form && opts.form instanceof HTMLFormElement) {
|
|
23
|
+
url = opts.form.action;
|
|
24
|
+
method = opts.form.method ?? 'get';
|
|
25
|
+
if (opts.form.attributes.enctype) contentType = opts.form.attributes.enctype;
|
|
26
|
+
data = new FormData(opts.form);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Append CSRF Token
|
|
30
|
+
if (!csrfToken) {
|
|
31
|
+
const resp = await fetch(url, {method: 'head'});
|
|
32
|
+
const newToken = resp.headers.get('x-csrf-token');
|
|
33
|
+
if (newToken) setCsrfToken(newToken);
|
|
34
|
+
}
|
|
35
|
+
if (data instanceof FormData) {
|
|
36
|
+
data.append('_token', csrfToken);
|
|
37
|
+
} else {
|
|
38
|
+
data._token = csrfToken;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Convert data
|
|
42
|
+
if (contentType == 'application/json' || contentType == 'text/plain') {
|
|
43
|
+
if (data instanceof FormData) {
|
|
44
|
+
const output = {};
|
|
45
|
+
data.forEach((value, key) => output[key] = value);
|
|
46
|
+
data = output;
|
|
47
|
+
}
|
|
48
|
+
data = JSON.stringify(data);
|
|
49
|
+
|
|
50
|
+
} else { // multipart, urlencoded
|
|
51
|
+
if (!(data instanceof FormData)) {
|
|
52
|
+
const output = new FormData();
|
|
53
|
+
for (let key in item) {
|
|
54
|
+
output.append(key, item[key]);
|
|
55
|
+
}
|
|
56
|
+
data = output;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Assemble the request
|
|
61
|
+
const request = new Request(url, {
|
|
62
|
+
method: method,
|
|
63
|
+
headers: {
|
|
64
|
+
"Accept": "application/json",
|
|
65
|
+
"Content-Type": contentType
|
|
66
|
+
},
|
|
67
|
+
body: data
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Append JWT Token
|
|
71
|
+
const jwtToken = localStorage.getItem('token');
|
|
72
|
+
if (jwtToken) request.headers.append("Authorization", "Bearer " + jwtToken);
|
|
73
|
+
|
|
74
|
+
// Get the response
|
|
75
|
+
const response = await fetch(request);
|
|
76
|
+
|
|
77
|
+
// Save new CSRF Token for later
|
|
78
|
+
const newToken = response.headers.get('x-csrf-token');
|
|
79
|
+
if (newToken) setCsrfToken(newToken);
|
|
80
|
+
|
|
81
|
+
if (!response.ok) {
|
|
82
|
+
throw new Error(response.statusText);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// const contentType = response.headers.get('content-type');
|
|
86
|
+
// if (contentType && contentType.includes('application/json')) {
|
|
87
|
+
// return response.json();
|
|
88
|
+
// } else {
|
|
89
|
+
// return response.text();
|
|
90
|
+
// }
|
|
91
|
+
return response.json();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export default class BsForm extends BsElement {
|
|
95
|
+
|
|
96
|
+
static get properties() {
|
|
97
|
+
return {
|
|
98
|
+
method: {type: String},
|
|
99
|
+
action: {type: String},
|
|
100
|
+
submit: {},
|
|
101
|
+
controls: {},
|
|
102
|
+
_wasChanged: {type: Boolean, state: false}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
connectedCallback() {
|
|
107
|
+
super.connectedCallback();
|
|
108
|
+
this.addEventListener('change', this.changeListener);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
disconnectedCallback() {
|
|
112
|
+
this.removeEventListener('change', this.changeListener);
|
|
113
|
+
super.disconnectedCallback();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
changeListener(e) {
|
|
117
|
+
this._wasChanged = true;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
handleSubmit(e) {
|
|
121
|
+
const form = e.target;
|
|
122
|
+
form.classList.add('was-validated');
|
|
123
|
+
if (form.checkValidity() === false) {
|
|
124
|
+
e.preventDefault();
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (this.submit) {
|
|
129
|
+
return this.submit(e);
|
|
130
|
+
|
|
131
|
+
} else {
|
|
132
|
+
httpRequest({form: form});
|
|
133
|
+
e.preventDefault();
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
firstUpdated() {
|
|
139
|
+
return html`
|
|
140
|
+
<form
|
|
141
|
+
method=${this.method}
|
|
142
|
+
action=${this.action}
|
|
143
|
+
@submit=${this.handleSubmit}
|
|
144
|
+
class="needs-validation"
|
|
145
|
+
>
|
|
146
|
+
${this.controls}
|
|
147
|
+
</form>
|
|
148
|
+
`;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
customElements.define("bs-form", BsForm);
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import BsElement from '../core/BsElement';
|
|
2
|
+
|
|
3
|
+
export class BsInput extends BsElement {
|
|
4
|
+
|
|
5
|
+
static get properties() {
|
|
6
|
+
return {
|
|
7
|
+
name: {type: String},
|
|
8
|
+
value: {},
|
|
9
|
+
label: {type: String},
|
|
10
|
+
type: {type: String},
|
|
11
|
+
title: {type: String},
|
|
12
|
+
options: {type: Array},
|
|
13
|
+
placeholder: {type: String},
|
|
14
|
+
checked: {type: Boolean},
|
|
15
|
+
readonly: {type: Boolean},
|
|
16
|
+
required: {type: Boolean},
|
|
17
|
+
disabled: {type: Boolean},
|
|
18
|
+
autofocus: {type: Boolean},
|
|
19
|
+
multiple: {type: Boolean},
|
|
20
|
+
variant: {type: String},
|
|
21
|
+
sizing: {type: String},
|
|
22
|
+
size: {type: Number},
|
|
23
|
+
maxlength: {type: Number},
|
|
24
|
+
minlength: {type: Number},
|
|
25
|
+
min: {type: Number},
|
|
26
|
+
max: {type: Number},
|
|
27
|
+
step: {type: Number},
|
|
28
|
+
pattern: {type: String},
|
|
29
|
+
accept: {type: String},
|
|
30
|
+
autocomplete: {type: Boolean},
|
|
31
|
+
content: {},
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
firstUpdated() {
|
|
36
|
+
if (this.variant == 'floating') {
|
|
37
|
+
this.classList.add('form-floating');
|
|
38
|
+
this.classList.add('d-block');
|
|
39
|
+
}
|
|
40
|
+
if (this.variant == 'horizontal') {
|
|
41
|
+
this.classList.add('row');
|
|
42
|
+
}
|
|
43
|
+
const id = "input_" + Math.random().toString(36).substr(2);
|
|
44
|
+
|
|
45
|
+
this.name = this.name ?? "";
|
|
46
|
+
this.value = this.value ?? "";
|
|
47
|
+
this.label = this.label ?? this.name;
|
|
48
|
+
this.options = this.options ?? [];
|
|
49
|
+
|
|
50
|
+
if (!this.type) this.type = typeof this.value;
|
|
51
|
+
if (this.type == 'boolean') this.type = 'checkbox';
|
|
52
|
+
if (this.type == 'number') this.type = 'number';
|
|
53
|
+
if (this.type == 'string') this.type = 'text';
|
|
54
|
+
if (this.type == 'object' && this.value instanceof Date) this.type = 'datetime-local';
|
|
55
|
+
|
|
56
|
+
const label = html`
|
|
57
|
+
<label for=${id} class=${
|
|
58
|
+
(this.variant == '' ? "form-label" : "") +
|
|
59
|
+
(this.variant == 'horizontal' ? "col-sm-3 col-form-label" : "") +
|
|
60
|
+
(this.sizing ? ' col-form-label-' + this.sizing : '')
|
|
61
|
+
}>
|
|
62
|
+
${(this.type == 'checkbox') ? nothing : this.label}
|
|
63
|
+
</label>
|
|
64
|
+
`;
|
|
65
|
+
|
|
66
|
+
let control;
|
|
67
|
+
switch (this.type) {
|
|
68
|
+
case 'custom':
|
|
69
|
+
control = html`${this.content}`
|
|
70
|
+
break;
|
|
71
|
+
|
|
72
|
+
case 'group':
|
|
73
|
+
control = html`<div class=${"input-group" + (this.sizing ? " input-group-"+this.sizing : "")}>${this.content}</div>`
|
|
74
|
+
break;
|
|
75
|
+
|
|
76
|
+
case 'radio':
|
|
77
|
+
control = this.options.map(option => html`
|
|
78
|
+
<div class="form-check">
|
|
79
|
+
<input id="${id}_${option.value}"
|
|
80
|
+
class="form-check-input"
|
|
81
|
+
type="radio"
|
|
82
|
+
name=${this.name}
|
|
83
|
+
value=${option.value}
|
|
84
|
+
?checked=${(option.checked || this.value === option.value) ?? false}
|
|
85
|
+
>
|
|
86
|
+
<label for=${id + "_" + option.value} class="form-check-label">${option.text ?? option.value}</label>
|
|
87
|
+
</div>`);
|
|
88
|
+
break;
|
|
89
|
+
|
|
90
|
+
case 'checkbox':
|
|
91
|
+
control = html`
|
|
92
|
+
<div class="form-check">
|
|
93
|
+
<input id=${id}
|
|
94
|
+
class="form-check-input"
|
|
95
|
+
type="checkbox"
|
|
96
|
+
name=${this.name}
|
|
97
|
+
value=${this.value}
|
|
98
|
+
?checked=${this.checked ?? false}
|
|
99
|
+
>
|
|
100
|
+
<label for=${id} class="form-check-label">${this.label}</label>
|
|
101
|
+
</div>`;
|
|
102
|
+
break;
|
|
103
|
+
|
|
104
|
+
case 'textarea':
|
|
105
|
+
control = html`
|
|
106
|
+
<textarea id=${id}
|
|
107
|
+
class=${"form-control" + (this.sizing ? ' form-control-' + this.sizing : '')}
|
|
108
|
+
name=${this.name}
|
|
109
|
+
.value=${this.value}
|
|
110
|
+
placeholder=${this.placeholder ?? nothing}
|
|
111
|
+
?readonly=${this.readonly ?? false}
|
|
112
|
+
?required=${this.required ?? false}
|
|
113
|
+
?disabled=${this.disabled ?? false}
|
|
114
|
+
?autofocus=${this.autofocus ?? false}
|
|
115
|
+
cols=${this.size ?? nothing}
|
|
116
|
+
maxlength=${this.maxlength ?? nothing}
|
|
117
|
+
></textarea>`;
|
|
118
|
+
break;
|
|
119
|
+
|
|
120
|
+
case 'select':
|
|
121
|
+
control = html`
|
|
122
|
+
<select id=${id}
|
|
123
|
+
class=${'form-select' + (this.sizing ? ' form-select-' + this.sizing : '')}
|
|
124
|
+
name=${this.name}
|
|
125
|
+
placeholder=${this.placeholder ?? nothing}
|
|
126
|
+
?required=${this.required ?? false}
|
|
127
|
+
?disabled=${this.disabled ?? false}
|
|
128
|
+
?autofocus=${this.autofocus ?? false}
|
|
129
|
+
?multiple=${this.multiple ?? false}
|
|
130
|
+
size=${this.size ?? nothing}
|
|
131
|
+
>
|
|
132
|
+
${this.options.map(option => html`
|
|
133
|
+
<option value=${option.value ?? ""} ?checked=${option.checked || this.value === option.value}>
|
|
134
|
+
${option.text ?? option.value}
|
|
135
|
+
</option>`
|
|
136
|
+
)}
|
|
137
|
+
</select>`;
|
|
138
|
+
break;
|
|
139
|
+
|
|
140
|
+
case 'text':
|
|
141
|
+
case 'url':
|
|
142
|
+
case 'email':
|
|
143
|
+
case 'password':
|
|
144
|
+
case 'file':
|
|
145
|
+
case 'hidden':
|
|
146
|
+
case 'number':
|
|
147
|
+
case 'range':
|
|
148
|
+
case 'color':
|
|
149
|
+
case 'tel':
|
|
150
|
+
case 'date':
|
|
151
|
+
case' datetime-local':
|
|
152
|
+
case 'week':
|
|
153
|
+
case 'month':
|
|
154
|
+
case 'time':
|
|
155
|
+
default:
|
|
156
|
+
control = html`
|
|
157
|
+
<input id=${id}
|
|
158
|
+
class=${(this.type == 'range' ? 'form-range' : "form-control") +
|
|
159
|
+
(this.type == 'color' ? ' form-control-color' : "") +
|
|
160
|
+
(this.sizing ? ' form-control-' + this.sizing : '')
|
|
161
|
+
}
|
|
162
|
+
type=${this.type}
|
|
163
|
+
name=${this.name}
|
|
164
|
+
.value=${this.value}
|
|
165
|
+
list=${this.options.length > 0 ? id + "_list" : nothing}
|
|
166
|
+
placeholder=${this.placeholder ?? nothing}
|
|
167
|
+
pattern=${this.pattern ?? nothing}
|
|
168
|
+
accept=${this.accept ?? nothing}
|
|
169
|
+
?readonly=${this.readonly ?? false}
|
|
170
|
+
?required=${this.required ?? false}
|
|
171
|
+
?disabled=${this.disabled ?? false}
|
|
172
|
+
?autofocus=${this.autofocus ?? false}
|
|
173
|
+
?multiple=${this.multiple ?? false}
|
|
174
|
+
autocomplete=${!!this.autocomplete ? 'off' : nothing}
|
|
175
|
+
size=${this.size ?? nothing}
|
|
176
|
+
min=${this.min ?? nothing}
|
|
177
|
+
max=${this.max ?? nothing}
|
|
178
|
+
step=${this.step ?? nothing}
|
|
179
|
+
maxlength=${this.maxlength ?? nothing}
|
|
180
|
+
minlength=${this.minlength ?? nothing}
|
|
181
|
+
title=${this.title ?? nothing}
|
|
182
|
+
>
|
|
183
|
+
${this.options.length > 0 ? html`
|
|
184
|
+
<datalist id=${id + "_list"}>
|
|
185
|
+
${this.options.map(option => html`
|
|
186
|
+
<option value=${option.value ?? ""}></option>
|
|
187
|
+
`)}
|
|
188
|
+
</datalist>` : ''}`;
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return (this.variant == 'floating')
|
|
193
|
+
? html`
|
|
194
|
+
${control}
|
|
195
|
+
${label}
|
|
196
|
+
${this.title ? html`<div class="invalid-feedback">${this.title}</div>` : nothing}
|
|
197
|
+
`
|
|
198
|
+
: html`
|
|
199
|
+
${label}
|
|
200
|
+
<div class=${this.variant == 'horizontal' ? "col-sm-9" : ""}>
|
|
201
|
+
${control}
|
|
202
|
+
${this.title ? html`<div class="invalid-feedback">${this.title}</div>` : nothing}
|
|
203
|
+
</div>
|
|
204
|
+
`
|
|
205
|
+
;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
customElements.define('bs-input', BsInput);
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import BsElement from "../core/BsElement.js";
|
|
2
|
+
|
|
3
|
+
export default class BsTable extends BsElement {
|
|
4
|
+
|
|
5
|
+
static get properties() {
|
|
6
|
+
return {
|
|
7
|
+
tableClass: {type: String},
|
|
8
|
+
editable: {type: Boolean},
|
|
9
|
+
filterable: {type: Boolean},
|
|
10
|
+
src: {type: String},
|
|
11
|
+
data: {type: Array},
|
|
12
|
+
|
|
13
|
+
columns: { type: Array },
|
|
14
|
+
index: { type: Number, state: true },
|
|
15
|
+
unique: { type: Array, state: true },
|
|
16
|
+
filter: { type: Array, state: true },
|
|
17
|
+
hidden: { type: Array, state: true }
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
constructor() {
|
|
22
|
+
super();
|
|
23
|
+
this.tableClass = '';
|
|
24
|
+
this.editable = false;
|
|
25
|
+
this.filterable = false;
|
|
26
|
+
this.src = '';
|
|
27
|
+
this.data = [];
|
|
28
|
+
|
|
29
|
+
this.columns = [];
|
|
30
|
+
this.index = null;
|
|
31
|
+
this.unique = [];
|
|
32
|
+
this.filter = [];
|
|
33
|
+
this.hidden = [];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async firstUpdated() {
|
|
37
|
+
if (!this.data) await this.fetchData();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Download the latest json and update it locally
|
|
41
|
+
async fetchData() {
|
|
42
|
+
let _data;
|
|
43
|
+
if (this.src.length > 0) {
|
|
44
|
+
// If a src attribute is set prefer it over any slots
|
|
45
|
+
_data = await fetch(this.src).then((res) => res.json());
|
|
46
|
+
} else {
|
|
47
|
+
// If no src attribute is set then grab the inline json in the slot
|
|
48
|
+
const elem = this.parentElement?.querySelector(
|
|
49
|
+
'script[type="application/json"]'
|
|
50
|
+
);
|
|
51
|
+
if (elem) _data = JSON.parse(elem.innerHTML);
|
|
52
|
+
}
|
|
53
|
+
this.data = this.transform(_data ?? []);
|
|
54
|
+
this.requestUpdate();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
transform(data) {
|
|
58
|
+
if (!this.columns) {
|
|
59
|
+
this.columns = Object.keys(this.data[0]).map(key => {
|
|
60
|
+
return {key: key, label: key}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
return data;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
renderFilter() {
|
|
67
|
+
return html`
|
|
68
|
+
<input type="search">
|
|
69
|
+
<ul>
|
|
70
|
+
<li class=${(this.filter[this.index] === undefined) ? 'disable' : ''} @click=${this.onApply}></li>
|
|
71
|
+
${this.unique.map(cell => {
|
|
72
|
+
const hidden = this.filter[this.index] !== undefined && this.filter[this.index] !== cell;
|
|
73
|
+
return html`
|
|
74
|
+
<li class=${hidden ? 'disable' : ''} @click=${this.onApply}>${cell}</li>
|
|
75
|
+
`;
|
|
76
|
+
})}
|
|
77
|
+
</ul>
|
|
78
|
+
`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
onInput (e) {
|
|
82
|
+
const value = e.target.value;
|
|
83
|
+
const key = row[0];
|
|
84
|
+
const current = this.data[index];
|
|
85
|
+
current[key] = value;
|
|
86
|
+
this.data[index] = current;
|
|
87
|
+
const args = {
|
|
88
|
+
detail: {
|
|
89
|
+
index: index,
|
|
90
|
+
data: current,
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
this.requestUpdate();
|
|
94
|
+
|
|
95
|
+
if (this.handleInputCell)
|
|
96
|
+
this.handleInputCell(args);
|
|
97
|
+
else
|
|
98
|
+
this.dispatchEvent(
|
|
99
|
+
new CustomEvent("input-cell", args)
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
renderColumns () {
|
|
104
|
+
return html`
|
|
105
|
+
<tr>
|
|
106
|
+
${this.columns.map((cell, index) => {
|
|
107
|
+
return html`
|
|
108
|
+
<th data-key=${cell.key} class=${(this.filter[index] !== undefined) ? 'active' : ''}>
|
|
109
|
+
${cell.label}
|
|
110
|
+
<i class=${this.index === index ? 'arrow_drop_up' : 'arrow_drop_down'} @click=${this.onSelect}></i>
|
|
111
|
+
<div>
|
|
112
|
+
${this.filterable && this.index === index ? this.renderFilter() : ''}
|
|
113
|
+
</div>
|
|
114
|
+
</th>
|
|
115
|
+
`
|
|
116
|
+
})}
|
|
117
|
+
</tr>
|
|
118
|
+
`;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
renderRow(entry, index) {
|
|
122
|
+
const matches = entry.every((cell, index) => {
|
|
123
|
+
return this.filter[index] === undefined || this.filter[index] == cell;
|
|
124
|
+
});
|
|
125
|
+
if(matches) {
|
|
126
|
+
this.hidden[index] = true;
|
|
127
|
+
return html`
|
|
128
|
+
<tr>
|
|
129
|
+
${entry.map(cell => {
|
|
130
|
+
return html`
|
|
131
|
+
<td>
|
|
132
|
+
${this.editable
|
|
133
|
+
? html`<input value="${cell}" type="text" @input=${this.onInput} />`
|
|
134
|
+
: html`${cell}`
|
|
135
|
+
}
|
|
136
|
+
</td>
|
|
137
|
+
`;
|
|
138
|
+
})}
|
|
139
|
+
</tr>
|
|
140
|
+
`;
|
|
141
|
+
}
|
|
142
|
+
delete this.hidden[index];
|
|
143
|
+
return '';
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
onSelect(event) {
|
|
147
|
+
if(this.head.includes(event.target)) {
|
|
148
|
+
const index = this.head.indexOf(event.target);
|
|
149
|
+
if(this.index === null || this.index !== index) {
|
|
150
|
+
this.index = this.head.indexOf(event.target);
|
|
151
|
+
let column = this.data.map(row => row[index]);
|
|
152
|
+
// column = column.filter((row, index) => this.hidden[index]);
|
|
153
|
+
this.unique = [...new Set(column)];
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
this.index = null;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
onApply(event) {
|
|
162
|
+
if(!event.target.classList.contains('disable')) {
|
|
163
|
+
const column = event.target.parentNode.parentNode;
|
|
164
|
+
const index = this.head.indexOf(column);
|
|
165
|
+
const value = event.target.textContent;
|
|
166
|
+
value === '' ? delete this.filter[index] : this.filter[index] = value;
|
|
167
|
+
this.index = null;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
get table() {
|
|
172
|
+
return this.renderRoot.querySelector('table');
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
get head() {
|
|
176
|
+
return Array.from(this.table.querySelectorAll('thead th'));
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
get body() {
|
|
180
|
+
return Array.from(this.table.querySelectorAll('tbody tr'));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
firstUpdated() {
|
|
184
|
+
return html`
|
|
185
|
+
<div class="table-responsive">
|
|
186
|
+
<slot name="a"></slot>
|
|
187
|
+
<table class=${"table " + this.tableClass}>
|
|
188
|
+
<thead>
|
|
189
|
+
${this.renderColumns()}
|
|
190
|
+
</thead>
|
|
191
|
+
<tbody>
|
|
192
|
+
${!this.data
|
|
193
|
+
? html`<tr><td colspan=${this.columns.length}>Loading...</td></tr>`
|
|
194
|
+
: nothing
|
|
195
|
+
}
|
|
196
|
+
${this.data.length === 0
|
|
197
|
+
? html`<tr><td colspan=${this.columns.length}>No Items Found!</td></tr>`
|
|
198
|
+
: nothing
|
|
199
|
+
}
|
|
200
|
+
${this.data.map(
|
|
201
|
+
this.renderRow
|
|
202
|
+
)}
|
|
203
|
+
</tbody>
|
|
204
|
+
</table>
|
|
205
|
+
</div>
|
|
206
|
+
`;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
customElements.define("bs-table", BsTable);
|
|
File without changes
|
|
File without changes
|