@hotstaq/admin-panel 0.2.12 → 0.3.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.
Files changed (36) hide show
  1. package/build/WebExport.d.ts.map +1 -1
  2. package/build/WebExport.js +4 -3
  3. package/build/WebExport.js.map +1 -1
  4. package/build/components/admin-button.d.ts +8 -0
  5. package/build/components/admin-button.d.ts.map +1 -1
  6. package/build/components/admin-button.js +23 -1
  7. package/build/components/admin-button.js.map +1 -1
  8. package/build/components/admin-dropdown.d.ts +30 -0
  9. package/build/components/admin-dropdown.d.ts.map +1 -0
  10. package/build/components/admin-dropdown.js +89 -0
  11. package/build/components/admin-dropdown.js.map +1 -0
  12. package/build/components/admin-edit.d.ts +23 -6
  13. package/build/components/admin-edit.d.ts.map +1 -1
  14. package/build/components/admin-edit.js +84 -55
  15. package/build/components/admin-edit.js.map +1 -1
  16. package/build/components/admin-table.d.ts +2 -2
  17. package/build/components/admin-table.d.ts.map +1 -1
  18. package/build/components/admin-table.js +10 -8
  19. package/build/components/admin-table.js.map +1 -1
  20. package/build/components/admin-text.d.ts +5 -12
  21. package/build/components/admin-text.d.ts.map +1 -1
  22. package/build/components/admin-text.js +8 -4
  23. package/build/components/admin-text.js.map +1 -1
  24. package/build/index.d.ts +2 -1
  25. package/build/index.d.ts.map +1 -1
  26. package/build/index.js +3 -1
  27. package/build/index.js.map +1 -1
  28. package/build-web/AdminPanelComponents.js +2 -2
  29. package/package.json +1 -1
  30. package/src/WebExport.ts +4 -3
  31. package/src/components/admin-button.ts +37 -1
  32. package/src/components/admin-dropdown.ts +115 -0
  33. package/src/components/admin-edit.ts +118 -71
  34. package/src/components/admin-table.ts +12 -11
  35. package/src/components/admin-text.ts +15 -16
  36. package/src/index.ts +2 -0
@@ -2,19 +2,55 @@ import { HotStaq, Hot, HotAPI, HotComponent } from "hotstaq";
2
2
 
3
3
  export class AdminButton extends HotComponent
4
4
  {
5
+ /**
6
+ * Executes when the button is clicked.
7
+ */
8
+ onclick: (event: MouseEvent) => Promise<any>;
9
+
5
10
  constructor (copy: HotComponent | HotStaq, api: HotAPI)
6
11
  {
7
12
  super (copy, api);
8
13
 
9
14
  this.tag = "admin-button";
15
+ this.onclick = null;
10
16
  }
11
17
 
12
18
  async buttonClicked (): Promise<void>
13
19
  {
14
20
  }
15
21
 
22
+ /**
23
+ * Corrects the placement of the text elements for modals.
24
+ */
25
+ onPostPlace (parentHtmlElement: HTMLElement, htmlElement: HTMLElement): HTMLElement
26
+ {
27
+ let placeHereArray = parentHtmlElement.querySelectorAll (`hot-place-here[type="modal"]`);
28
+
29
+ // Search for the input box in the modal we attached to, then store the
30
+ // found input box into the fieldElements array.
31
+ if (placeHereArray.length > 0)
32
+ {
33
+ let placeHere = placeHereArray[0];
34
+ parentHtmlElement.removeChild (htmlElement);
35
+ placeHere.appendChild (htmlElement);
36
+
37
+ // @ts-ignore
38
+ parentHtmlElement.hotComponent.fieldElements[this.field] = htmlElement;
39
+ }
40
+
41
+ if (this.onclick != null)
42
+ {
43
+ if (typeof (this.onclick) === "string")
44
+ this.onclick = (<(event: MouseEvent) => Promise<any>>new Function (this.onclick));
45
+
46
+ htmlElement.addEventListener ("click", this.onclick);
47
+ }
48
+
49
+ return (null);
50
+ }
51
+
16
52
  output (): string
17
53
  {
18
- return (`<button id = "${this.htmlElements[0].id}" onclick = "this.buttonClicked ();"></button>`);
54
+ return (`<button id = "${this.htmlElements[0].id}" onclick = "this.buttonClicked ();">${this.inner}</button>`);
19
55
  }
20
56
  }
@@ -0,0 +1,115 @@
1
+ import { HotStaq, Hot, HotAPI, HotComponent } from "hotstaq";
2
+
3
+ export class AdminDropdown extends HotComponent
4
+ {
5
+ /**
6
+ * The search string placeholder to show.
7
+ */
8
+ placeholder: string;
9
+ /**
10
+ * Executes when the keyboard up button is triggered.
11
+ */
12
+ onsearch: (searchText: string, event: KeyboardEvent) => Promise<{ url: string; value: string; text: string; }[]>;
13
+ /**
14
+ * Executes when an search result has been selected.
15
+ */
16
+ onselect: (url: string, value: string, text: string) => Promise<void>;
17
+
18
+ constructor (copy: HotComponent | HotStaq, api: HotAPI)
19
+ {
20
+ super (copy, api);
21
+
22
+ this.tag = "admin-dropdown";
23
+ this.placeholder = "Search";
24
+ this.onsearch = null;
25
+ this.onselect = null;
26
+ }
27
+
28
+ /**
29
+ * Corrects the placement of the text elements for modals.
30
+ */
31
+ onPostPlace (parentHtmlElement: HTMLElement, htmlElement: HTMLElement): HTMLElement
32
+ {
33
+ let placeHereArray = parentHtmlElement.querySelectorAll (`hot-place-here[type="modal"]`);
34
+
35
+ // Search for the input box in the modal we attached to, then store the
36
+ // found input box into the fieldElements array.
37
+ if (placeHereArray.length > 0)
38
+ {
39
+ let placeHere = placeHereArray[0];
40
+ parentHtmlElement.removeChild (htmlElement);
41
+ placeHere.appendChild (htmlElement);
42
+
43
+ // @ts-ignore
44
+ parentHtmlElement.hotComponent.fieldElements[this.field] = htmlElement;
45
+ }
46
+
47
+ if (this.onsearch != null)
48
+ {
49
+ if (typeof (this.onsearch) === "string")
50
+ this.onsearch = (<(searchText: string, event: KeyboardEvent) => Promise<{ url: string; value: string; text: string; }[]>>new Function (this.onsearch));
51
+
52
+ htmlElement.querySelector ("input").addEventListener ("keyup",
53
+ async (event: KeyboardEvent) =>
54
+ {
55
+ // @ts-ignore
56
+ const searchText = event.currentTarget.value;
57
+
58
+ let results = await this.onsearch (searchText, event);
59
+
60
+ let searchResultsDiv = $(this.htmlElements[1]).find (".searchResults");
61
+
62
+ searchResultsDiv.empty ();
63
+
64
+ for (let iIdx = 0; iIdx < results.length; iIdx++)
65
+ {
66
+ const result = results[iIdx];
67
+ let url: string = result.url;
68
+ let value: string = result.value;
69
+ let text: string = result.text;
70
+
71
+ searchResultsDiv.append ($(`<li><a class=\"dropdown-item\" href=\"${url}\" data-value = "${value}" onclick = \"this.parentNode.parentNode.parentNode.parentNode.hotComponent.selected (${iIdx});\">${text}</a></li>`));
72
+ }
73
+ });
74
+ }
75
+
76
+ return (null);
77
+ }
78
+
79
+ /**
80
+ * Executed when an item is selected.
81
+ */
82
+ async selected (index: number): Promise<void>
83
+ {
84
+ if (this.onselect != null)
85
+ {
86
+ if (typeof (this.onselect) === "string")
87
+ this.onselect = (<(url: string, value: string, text: string) => Promise<void>>new Function (this.onselect));
88
+
89
+ const url = this.htmlElements[1].querySelectorAll ("a")[index].getAttribute ("href");
90
+ const value = this.htmlElements[1].querySelectorAll ("a")[index].getAttribute ("data-value");
91
+ const text = this.htmlElements[1].querySelectorAll ("a")[index].innerHTML;
92
+
93
+ await this.onselect (url, value, text);
94
+ }
95
+ }
96
+
97
+ output (): string
98
+ {
99
+ return (
100
+ `<div class="dropdown">
101
+ <button class="btn btn-primary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
102
+ ${this.inner}
103
+ </button>
104
+ <ul class="dropdown-menu">
105
+ <li>
106
+ <div class = "input-group">
107
+ <input type = "text" class = "form-control" placeholder = "${this.placeholder}" value = "" />
108
+ </div>
109
+ </li>
110
+ <div class = "searchResults">
111
+ </div>
112
+ </ul>
113
+ </div>`);
114
+ }
115
+ }
@@ -22,14 +22,14 @@ export class AdminEdit extends HotComponent
22
22
  * The attached schema.
23
23
  */
24
24
  schema: string;
25
- /**
26
- * The field elements in the edit modal.
27
- */
28
- fieldElements: { [name: string]: any; };
29
25
  /**
30
26
  * The modal id.
31
27
  */
32
28
  modalId: string;
29
+ /**
30
+ * The hot-class attribute to pass to add to the modal's classes.
31
+ */
32
+ class: string;
33
33
  /**
34
34
  * The bootstrap modal instance.
35
35
  */
@@ -49,6 +49,18 @@ export class AdminEdit extends HotComponent
49
49
  * The selected fields.
50
50
  */
51
51
  protected selectedFields: any[];
52
+ /**
53
+ * What to execute when the save button is clicked.
54
+ */
55
+ onsave: (modalType: string, values: any, selectedFields: any[]) => Promise<void>;
56
+ /**
57
+ * What to execute when the edit button is clicked.
58
+ */
59
+ onedit: (modalType: string, selectedFields: any[]) => Promise<boolean>;
60
+ /**
61
+ * What to execute when the delete button is clicked.
62
+ */
63
+ ondelete: (selectedField: any) => Promise<void>;
52
64
 
53
65
  constructor (copy: HotComponent | HotStaq, api: HotAPI)
54
66
  {
@@ -60,13 +72,38 @@ export class AdminEdit extends HotComponent
60
72
  this.attached_list = "";
61
73
  this.schema = "";
62
74
 
63
- this.fieldElements = {};
64
-
65
75
  this.modalId = "";
76
+ this.class = "";
66
77
  this.modal = null;
67
78
  this.modalType = "add";
68
79
  this.closeOnSave = true;
69
80
  this.selectedFields = [];
81
+ this.onsave = null;
82
+ this.onedit = null;
83
+ this.ondelete = null;
84
+ }
85
+
86
+ /**
87
+ * Process the hot fields.
88
+ */
89
+ async processHotFields (onField: (htmlElement: Element, field: { name: string; type: string; }) => Promise<void>): Promise<void>
90
+ {
91
+ const elms = this.htmlElements[1].querySelectorAll ("[hot-field]");
92
+
93
+ for (let iIdx = 0; iIdx < elms.length; iIdx++)
94
+ {
95
+ const elm = elms[iIdx];
96
+ const field = $(elm).attr ("hot-field");
97
+ let fieldType = $(elm).attr ("hot-field-type");
98
+
99
+ if ((fieldType == null) || (fieldType === ""))
100
+ fieldType = "text";
101
+
102
+ // @ts-ignore
103
+ elm.fieldType = fieldType;
104
+
105
+ await onField (elm, { name: field, type: fieldType });
106
+ }
70
107
  }
71
108
 
72
109
  /**
@@ -90,13 +127,10 @@ export class AdminEdit extends HotComponent
90
127
 
91
128
  this.modalType = "add";
92
129
 
93
- // Clear the values in each field.
94
- for (let key in this.fieldElements)
95
- {
96
- let fieldElement = this.fieldElements[key];
97
-
98
- fieldElement.value = "";
99
- }
130
+ await this.processHotFields (async (htmlElement: Element, field: { name: string; type: string; }) =>
131
+ {
132
+ $(htmlElement).val ("");
133
+ });
100
134
 
101
135
  this.selectedFields = [];
102
136
 
@@ -131,21 +165,31 @@ export class AdminEdit extends HotComponent
131
165
 
132
166
  if (selectedField != null)
133
167
  {
134
- for (let key in this.fieldElements)
135
- {
136
- let fieldElement = this.fieldElements[key];
137
-
138
- if (fieldElement != null)
168
+ await this.processHotFields (async (htmlElement: Element, field: { name: string; type: string; }) =>
139
169
  {
140
170
  // @ts-ignore
141
- let value = selectedField[key];
171
+ const value = selectedField[field.name];
142
172
 
143
- fieldElement.value = value;
144
- }
145
- }
173
+ if (value != null)
174
+ $(htmlElement).val (value);
175
+ });
146
176
 
147
177
  this.selectedFields = [selectedField];
148
178
 
179
+ if (this.onedit != null)
180
+ {
181
+ if (typeof (this.onedit) === "string")
182
+ this.onedit = (<(modalType: string, selectedFields: any[]) => Promise<boolean>>new Function (this.onedit));
183
+
184
+ let result = await this.onedit (this.modalType, this.selectedFields);
185
+
186
+ if (result != null)
187
+ {
188
+ if (result === false)
189
+ return;
190
+ }
191
+ }
192
+
149
193
  bootstrap.Modal.getInstance (`#${this.modalId}`).show ();
150
194
  }
151
195
  }
@@ -213,12 +257,22 @@ export class AdminEdit extends HotComponent
213
257
  }
214
258
  }
215
259
 
216
- let removeUrl: string = Hot.Data.AdminPanel.removeUrl;
260
+ if (this.ondelete != null)
261
+ {
262
+ if (typeof (this.ondelete) === "string")
263
+ this.ondelete = (<(selectedField: any) => Promise<void>>new Function (this.ondelete));
264
+
265
+ await this.ondelete (whereField);
266
+ }
267
+ else
268
+ {
269
+ let removeUrl: string = Hot.Data.AdminPanel.removeUrl;
217
270
 
218
- await Hot.jsonRequest (removeUrl, {
219
- schema: this.schema,
220
- whereFields: whereField
221
- });
271
+ await Hot.jsonRequest (removeUrl, {
272
+ schema: this.schema,
273
+ whereFields: whereField
274
+ });
275
+ }
222
276
  }
223
277
 
224
278
  await hotComponent.refreshList ();
@@ -233,67 +287,60 @@ export class AdminEdit extends HotComponent
233
287
  {
234
288
  let values: any = {};
235
289
 
236
- for (let key in this.fieldElements)
237
- {
238
- let fieldElement = this.fieldElements[key];
239
-
240
- if (fieldElement != null)
290
+ await this.processHotFields (async (htmlElement: Element, field: { name: string; type: string; }) =>
241
291
  {
242
- let value = fieldElement.value;
243
- let fieldType: string = fieldElement.parentNode.hotComponent.field_type;
292
+ if (field.type === "remove")
293
+ return;
244
294
 
245
- if (fieldType === "remove")
246
- continue;
247
-
248
- values[key] = value;
249
- }
250
- }
295
+ values[field.name] = $(htmlElement).val ();
296
+ });
251
297
 
252
- if (this.modalType === "add")
298
+ if (this.onsave != null)
253
299
  {
254
- let addUrl: string = Hot.Data.AdminPanel.addUrl;
300
+ if (typeof (this.onsave) === "string")
301
+ this.onsave = (<(modalType: string, values: any, selectedFields: any[]) => Promise<void>>new Function (this.onsave));
255
302
 
256
- await Hot.jsonRequest (addUrl, {
257
- schema: this.schema,
258
- fields: values
259
- });
303
+ await this.onsave (this.modalType, values, this.selectedFields);
260
304
  }
261
-
262
- if (this.modalType === "edit")
305
+ else
263
306
  {
264
- if (this.selectedFields.length === 0)
307
+ if (this.modalType === "add")
265
308
  {
266
- alert ("No item(s) selected!");
309
+ let addUrl: string = Hot.Data.AdminPanel.addUrl;
267
310
 
268
- return;
311
+ await Hot.jsonRequest (addUrl, {
312
+ schema: this.schema,
313
+ fields: values
314
+ });
269
315
  }
270
316
 
271
- let selectedField = this.selectedFields[0];
272
- let whereFields: any = {};
273
-
274
- for (let key in selectedField)
317
+ if (this.modalType === "edit")
275
318
  {
276
- let fieldElement = this.fieldElements[key];
277
- let value = selectedField[key];
278
-
279
- if (fieldElement != null)
319
+ if (this.selectedFields.length === 0)
280
320
  {
281
- let fieldType: string = fieldElement.parentNode.hotComponent.field_type;
321
+ alert ("No item(s) selected!");
282
322
 
283
- if (fieldType === "remove")
284
- continue;
323
+ return;
285
324
  }
286
325
 
287
- whereFields[key] = value;
288
- }
326
+ let whereFields: any = {};
289
327
 
290
- let editUrl: string = Hot.Data.AdminPanel.editUrl;
328
+ await this.processHotFields (async (htmlElement: Element, field: { name: string; type: string; }) =>
329
+ {
330
+ if (field.type === "remove")
331
+ return;
291
332
 
292
- await Hot.jsonRequest (editUrl, {
293
- schema: this.schema,
294
- whereFields: whereFields,
295
- fields: values
296
- });
333
+ whereFields[field.name] = $(htmlElement).val ();
334
+ });
335
+
336
+ let editUrl: string = Hot.Data.AdminPanel.editUrl;
337
+
338
+ await Hot.jsonRequest (editUrl, {
339
+ schema: this.schema,
340
+ whereFields: whereFields,
341
+ fields: values
342
+ });
343
+ }
297
344
  }
298
345
 
299
346
  let attachedList = document.getElementById (this.attached_list);
@@ -332,7 +379,7 @@ export class AdminEdit extends HotComponent
332
379
  return ([{
333
380
  html: `
334
381
  <!-- ${this.title} Modal Start -->
335
- <div class="modal fade" id="${this.modalId}" tabindex="-1" aria-labelledby="${this.name}ModalLabel" aria-hidden="true">
382
+ <div class="modal fade ${this.class}" id="${this.modalId}" tabindex="-1" aria-labelledby="${this.name}ModalLabel" aria-hidden="true">
336
383
  <div class="modal-dialog">
337
384
  <div class="modal-content">
338
385
  <div class="modal-header">
@@ -55,9 +55,9 @@ export class AdminTable extends HotComponent
55
55
  */
56
56
  protected selected: number;
57
57
  /**
58
- * The list data to use for this table.
58
+ * The list function to execute to retrieve data.
59
59
  */
60
- listdata: string;
60
+ onlist: () => Promise<any[]>;;
61
61
  /**
62
62
  * The list url to use for this table. Hot.Data.AdminPanel.listUrl will not be used in this case.
63
63
  */
@@ -87,7 +87,7 @@ export class AdminTable extends HotComponent
87
87
  };
88
88
  this.rowElements = [];
89
89
  //this.selectedRows = [];
90
- this.listdata = "";
90
+ this.onlist = null;
91
91
  this.listurl = "";
92
92
  this.checkbox = true;
93
93
  this.onselectedrow = null;
@@ -286,6 +286,8 @@ export class AdminTable extends HotComponent
286
286
 
287
287
  if (this.checkbox === true)
288
288
  rowStr += `<td><input type = "checkbox" /></td>`;
289
+ else
290
+ rowStr += `<td></td>`;
289
291
 
290
292
  for (let iIdx = 0; iIdx < this.headers.indicies.length; iIdx++)
291
293
  {
@@ -370,6 +372,8 @@ export class AdminTable extends HotComponent
370
372
  */
371
373
  async clearRows ()
372
374
  {
375
+ this.rowElements = [];
376
+
373
377
  let tbody = this.htmlElements[1].getElementsByTagName ("tbody")[0];
374
378
 
375
379
  tbody.innerHTML = "";
@@ -382,12 +386,12 @@ export class AdminTable extends HotComponent
382
386
  {
383
387
  let list = null;
384
388
 
385
- if (this.listdata !== "")
389
+ if (this.onlist != null)
386
390
  {
387
- if (typeof (this.listdata) === "string")
388
- this.listdata = JSON.parse (this.listdata);
391
+ if (typeof (this.onlist) === "string")
392
+ this.onlist = (<() => Promise<any[]>>new Function (this.onlist));
389
393
 
390
- list = this.listdata;
394
+ list = await this.onlist ();
391
395
  }
392
396
  else
393
397
  {
@@ -426,10 +430,7 @@ export class AdminTable extends HotComponent
426
430
 
427
431
  output (): string | HotComponentOutput[]
428
432
  {
429
- let emptyCheckboxHeader: string = "";
430
-
431
- if (this.checkbox === true)
432
- emptyCheckboxHeader = "<th></th>";
433
+ let emptyCheckboxHeader: string = "<th></th>";
433
434
 
434
435
  return (`
435
436
  <div id = "${this.htmlElements[0].id}">
@@ -2,31 +2,23 @@ import { HotStaq, Hot, HotAPI, HotComponent, HotComponentOutput } from "hotstaq"
2
2
 
3
3
  export class AdminText extends HotComponent
4
4
  {
5
- /**
6
- * The associated database field.
7
- */
8
- field: string;
9
- /**
10
- * The type of field. Can be:
11
- * * text
12
- * * remove
13
- *
14
- * Default: text
15
- */
16
- field_type: string;
17
5
  /**
18
6
  * If set to 1, this will not output the field.
19
7
  */
20
8
  no_output: string;
9
+ /**
10
+ * The classes to set for the input.
11
+ * @default "form-control"
12
+ */
13
+ class: string;
21
14
 
22
15
  constructor (copy: HotComponent | HotStaq, api: HotAPI)
23
16
  {
24
17
  super (copy, api);
25
18
 
26
19
  this.tag = "admin-text";
27
- this.field = "";
28
- this.field_type = "text";
29
20
  this.no_output = "0";
21
+ this.class = "form-control"
30
22
  }
31
23
 
32
24
  /**
@@ -59,10 +51,17 @@ export class AdminText extends HotComponent
59
51
  value = this.value;
60
52
 
61
53
  if (this.no_output === "1")
62
- return (`<div><input class="form-control" type = "hidden" value = "${value}" /></div>`);
54
+ return (`<div><input class="${this.class}" type = "hidden" value = "${value}" /></div>`);
55
+
56
+ const field = this.htmlElements[0].getAttribute ("hot-field");
57
+ let field_type = this.htmlElements[0].getAttribute ("hot-field-type");
58
+
59
+ if (field_type == null)
60
+ field_type = "text";
63
61
 
64
62
  return (`<div>
65
- <label class="form-label">${this.inner}</label><input class="form-control" type = "text" value = "${value}" />
63
+ <label class="form-label">${this.inner}</label>
64
+ <input class="${this.class}" type = "text" hot-field = "${field}" hot-field-type = "${field_type}" value = "${value}" />
66
65
  </div>`);
67
66
  }
68
67
  }
package/src/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { AdminButton } from "./components/admin-button";
2
+ import { AdminDropdown } from "./components/admin-dropdown";
2
3
  import { AdminDashboard } from "./components/admin-dashboard";
3
4
  import { AdminEdit } from "./components/admin-edit";
4
5
  import { AdminTable } from "./components/admin-table";
@@ -8,6 +9,7 @@ import { AdminText } from "./components/admin-text";
8
9
 
9
10
  export {
10
11
  AdminButton,
12
+ AdminDropdown,
11
13
  AdminDashboard,
12
14
  AdminEdit,
13
15
  AdminTable,