@hashtagcms/admin-ui-kit 1.0.6

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 (79) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +83 -0
  3. package/dist/admin-ui-kit.min.css +14 -0
  4. package/dist/admin-ui-kit.min.js +2 -0
  5. package/dist/admin-ui-kit.min.js.LICENSE.txt +175 -0
  6. package/package.json +53 -0
  7. package/packages/components/README.md +92 -0
  8. package/packages/components/package.json +28 -0
  9. package/packages/components/src/action-bar.vue +237 -0
  10. package/packages/components/src/category-platform.vue +97 -0
  11. package/packages/components/src/category-settings.vue +815 -0
  12. package/packages/components/src/cms-module-dropdown.vue +78 -0
  13. package/packages/components/src/downlods.vue +21 -0
  14. package/packages/components/src/file-uploader.vue +188 -0
  15. package/packages/components/src/frontend-module-creator.vue +599 -0
  16. package/packages/components/src/global-site-button.vue +94 -0
  17. package/packages/components/src/homepage.vue +1087 -0
  18. package/packages/components/src/html-slot.vue +23 -0
  19. package/packages/components/src/image-gallery.vue +144 -0
  20. package/packages/components/src/index.js +53 -0
  21. package/packages/components/src/info-boxes.vue +68 -0
  22. package/packages/components/src/info-popup.vue +121 -0
  23. package/packages/components/src/language-button.vue +80 -0
  24. package/packages/components/src/language-copier.vue +177 -0
  25. package/packages/components/src/left-nav.vue +159 -0
  26. package/packages/components/src/library/copy-paste.vue +186 -0
  27. package/packages/components/src/library/info-box.vue +102 -0
  28. package/packages/components/src/library/left-menu-show-hide.vue +47 -0
  29. package/packages/components/src/library/loader.vue +141 -0
  30. package/packages/components/src/library/modal-box.vue +136 -0
  31. package/packages/components/src/library/split-button.vue +127 -0
  32. package/packages/components/src/library/timer-button.vue +43 -0
  33. package/packages/components/src/library/toast-box.vue +53 -0
  34. package/packages/components/src/menu-sorter.vue +265 -0
  35. package/packages/components/src/module-creator.vue +650 -0
  36. package/packages/components/src/module-permission.vue +334 -0
  37. package/packages/components/src/pagination.vue +125 -0
  38. package/packages/components/src/platform-button.vue +118 -0
  39. package/packages/components/src/search-bar.vue +144 -0
  40. package/packages/components/src/site-button.vue +42 -0
  41. package/packages/components/src/site-cloner.vue +150 -0
  42. package/packages/components/src/sitewise-copier.vue +234 -0
  43. package/packages/components/src/sitewise-data.vue +347 -0
  44. package/packages/components/src/sorter.vue +239 -0
  45. package/packages/components/src/tabular-view.vue +824 -0
  46. package/packages/components/src/title-bar.vue +76 -0
  47. package/packages/components/src/top-nav.vue +96 -0
  48. package/packages/helpers/README.md +88 -0
  49. package/packages/helpers/package.json +20 -0
  50. package/packages/helpers/src/admin-config.js +9 -0
  51. package/packages/helpers/src/common.js +89 -0
  52. package/packages/helpers/src/dashboard.js +16 -0
  53. package/packages/helpers/src/editor.js +163 -0
  54. package/packages/helpers/src/error-message-handler.js +50 -0
  55. package/packages/helpers/src/event-bus.js +4 -0
  56. package/packages/helpers/src/form.js +4 -0
  57. package/packages/helpers/src/fx.js +106 -0
  58. package/packages/helpers/src/humanize.js +14 -0
  59. package/packages/helpers/src/map.js +3 -0
  60. package/packages/styles/README.md +37 -0
  61. package/packages/styles/package.json +15 -0
  62. package/packages/styles/src/_action-bar.scss +35 -0
  63. package/packages/styles/src/_admin.scss +22 -0
  64. package/packages/styles/src/_animate.scss +1579 -0
  65. package/packages/styles/src/_badges.scss +34 -0
  66. package/packages/styles/src/_category-list.scss +14 -0
  67. package/packages/styles/src/_common.scss +163 -0
  68. package/packages/styles/src/_info-box.scss +96 -0
  69. package/packages/styles/src/_left-nav.scss +59 -0
  70. package/packages/styles/src/_loader.scss +82 -0
  71. package/packages/styles/src/_menu-sorter.scss +39 -0
  72. package/packages/styles/src/_model-creator.scss +48 -0
  73. package/packages/styles/src/_module-permission.scss +25 -0
  74. package/packages/styles/src/_page-manager.scss +63 -0
  75. package/packages/styles/src/_popover-modal.scss +20 -0
  76. package/packages/styles/src/_table-grid.scss +39 -0
  77. package/packages/styles/src/_toast.scss +20 -0
  78. package/packages/styles/src/_variables.scss +37 -0
  79. package/packages/styles/src/app.scss +2 -0
@@ -0,0 +1,76 @@
1
+ <template>
2
+ <div
3
+ class="btn-toolbar justify-content-between border-bottom mb-2 pb-2"
4
+ role="toolbar"
5
+ >
6
+ <div class="btn-group" role="group">
7
+ <small style="position: relative; top: 5px" v-if="showExpand">
8
+ <left-menu-toggle
9
+ data-icon-css="fa fa-arrow-left hand"
10
+ data-icon-css-off="fa fa-arrow-right hand"
11
+ ></left-menu-toggle
12
+ ></small>
13
+ <h4 class="moduleTitle" v-html="title"></h4>
14
+ </div>
15
+ <div class="input-group actionToolbar">
16
+ <copy-paste
17
+ :data-copy-paste-auto-init="dataCopyPasteAutoInit"
18
+ :data-show-copy="dataShowCopy"
19
+ :data-show-paste="dataShowPaste"
20
+ ></copy-paste>
21
+ <button
22
+ v-if="showBackButton"
23
+ aria-label="Back"
24
+ class="btn btn-outline-secondary"
25
+ title="Back"
26
+ type="button"
27
+ @click="goBack()"
28
+ >
29
+ {{ backTitle }}
30
+ </button>
31
+ </div>
32
+ </div>
33
+ </template>
34
+
35
+ <script>
36
+ import CopyPaste from "./library/copy-paste.vue";
37
+ import LeftMenuShowHide from "./library/left-menu-show-hide.vue";
38
+
39
+ export default {
40
+ components: {
41
+ "copy-paste": CopyPaste,
42
+ "left-menu-toggle": LeftMenuShowHide,
43
+ },
44
+ mounted() {},
45
+ props: [
46
+ "dataTitle",
47
+ "dataBackUrl",
48
+ "dataBackTitle",
49
+ "dataShowCopy",
50
+ "dataShowPaste",
51
+ "dataCopyPasteAutoInit",
52
+ "dataShowExpand",
53
+ ],
54
+ computed: {
55
+ showBackButton() {
56
+ return typeof this.dataBackUrl !== "undefined";
57
+ },
58
+ showExpand() {
59
+ return typeof this.dataShowExpand !== "undefined";
60
+ },
61
+ },
62
+ data() {
63
+ return {
64
+ title: this.dataTitle,
65
+ backUrl: this.dataBackUrl,
66
+ backTitle:
67
+ typeof this.dataBackTitle === "undefined" ? "Back" : this.dataBackTitle,
68
+ };
69
+ },
70
+ methods: {
71
+ goBack() {
72
+ window.location.href = this.backUrl;
73
+ },
74
+ },
75
+ };
76
+ </script>
@@ -0,0 +1,96 @@
1
+ <template>
2
+ <nav class="navbar navbar-expand-lg bg-light border-bottom">
3
+ <div class="container-fluid">
4
+ <a class="navbar-brand" href="/" target="_blank"
5
+ ><img class="cms-logo" :src="logo" :height="logoHeight" />
6
+ {{ siteName }}
7
+ </a>
8
+ <button
9
+ class="navbar-toggler"
10
+ type="button"
11
+ data-bs-toggle="collapse"
12
+ data-bs-target="#navbarScroll"
13
+ aria-controls="navbarScroll"
14
+ aria-expanded="false"
15
+ aria-label="Toggle navigation"
16
+ >
17
+ <span class="navbar-toggler-icon"></span>
18
+ </button>
19
+ <div class="collapse navbar-collapse" id="navbarScroll">
20
+ <ul
21
+ class="navbar-nav me-auto my-2 my-lg-0 navbar-nav-scroll"
22
+ style="--bs-scroll-height: 100px"
23
+ >
24
+ <li class="nav-item">
25
+ <global-site-button
26
+ :data-current-site="dataCurrentSite"
27
+ :data-supported-sites="dataSupportedSites"
28
+ :data-is-admin="dataIsAdmin"
29
+ ></global-site-button>
30
+ </li>
31
+ </ul>
32
+ <div class="d-flex">
33
+ Welcome &nbsp; <span class="text-success">{{ userName }}!</span>
34
+ <ul
35
+ class="navbar-nav me-auto my-2 my-lg-0 navbar-nav-scroll ms-1"
36
+ style="--bs-scroll-height: 100px"
37
+ >
38
+ <li class="nav-item">
39
+ <a href="/logout" @click.prevent="logout">Logout</a>
40
+ </li>
41
+ </ul>
42
+ </div>
43
+ </div>
44
+ </div>
45
+ </nav>
46
+ </template>
47
+
48
+ <script>
49
+ import AdminConfig from "@hashtagcms/helpers/admin-config";
50
+ import GlobalSiteButton from "./global-site-button.vue";
51
+ export default {
52
+ components: {
53
+ "global-site-button": GlobalSiteButton,
54
+ },
55
+ mounted() {
56
+ //console.log('dataCurrentSite '+this.dataCurrentSite);
57
+ },
58
+ props: [
59
+ "dataUsername",
60
+ "dataSiteName",
61
+ "dataCurrentSite",
62
+ "dataSupportedSites",
63
+ "dataIsAdmin",
64
+ "dataSiteCombo",
65
+ "dataLogo",
66
+ "dataLogoHeight",
67
+ ],
68
+ computed: {
69
+ logo() {
70
+ return typeof this.dataLogo !== "undefined" && this.dataLogo !== ""
71
+ ? this.dataLogo
72
+ : AdminConfig.admin_asset("img/logo-transparent.png");
73
+ },
74
+ },
75
+ data() {
76
+ return {
77
+ siteName: this.dataSiteName || "hashtagcms.org",
78
+ userName: this.dataUsername,
79
+ isLoggedIn: !!(this.dataUsername && this.dataUsername !== ""),
80
+ siteCombo: !(
81
+ typeof this.dataSiteCombo == "undefined" ||
82
+ this.dataSiteCombo === "false"
83
+ ),
84
+ logoHeight:
85
+ typeof this.dataLogoHeight === "undefined" || this.dataLogoHeight === ""
86
+ ? 50
87
+ : this.dataLogoHeight,
88
+ };
89
+ },
90
+ methods: {
91
+ logout(event) {
92
+ document.getElementById("logout-form").submit();
93
+ },
94
+ },
95
+ };
96
+ </script>
@@ -0,0 +1,88 @@
1
+ # @hashtagcms/helpers
2
+
3
+ A utility toolbelt for HashtagCMS development. This package provides essential classes for API communication, form handling, configuration management, and event management.
4
+
5
+ ## 📦 Installation
6
+
7
+ ```bash
8
+ npm install @hashtagcms/helpers
9
+ ```
10
+
11
+ ## 🚀 Usage
12
+
13
+ ```javascript
14
+ import { Toast, Loader } from "@hashtagcms/helpers/common";
15
+ import Form from "@hashtagcms/helpers/form";
16
+ import AdminConfig from "@hashtagcms/helpers/admin-config";
17
+ ```
18
+
19
+ ## 🛠 API Reference
20
+
21
+ ### `Form`
22
+
23
+ A robust class for handling form state, validation, and submission via Axios.
24
+
25
+ **Usage:**
26
+
27
+ ```javascript
28
+ let myForm = new Form({ name: "", email: "" });
29
+
30
+ // Submit
31
+ myForm
32
+ .post("/api/user/create")
33
+ .then((data) => console.log("Saved!"))
34
+ .catch((errors) => console.log(myForm.errors.get("email")));
35
+ ```
36
+
37
+ **Features:**
38
+
39
+ - **`post(url)`, `put(url)`, `delete(url)`**: wrappers for API calls.
40
+ - **`reset()`**: Clears fields and errors.
41
+ - **`errors.get('field')`**: Retrieve validation error for a field.
42
+ - **`data()`**: Returns the clean data object for submission.
43
+
44
+ ### `AdminConfig` (Default Export)
45
+
46
+ A singleton that manages application-wide settings passed from the server (usually via `window.Laravel`).
47
+
48
+ **Methods:**
49
+
50
+ - **`admin_path(path)`**: Generates a full URL for the admin panel.
51
+ - **`admin_asset(path)`**: Generates a URL for theme assets.
52
+ - **`get_media(path)`**: Generates a URL for media files.
53
+
54
+ ### `EventBus`
55
+
56
+ A lightweight wrapper around `mitt` to handle cross-component communication.
57
+
58
+ ```javascript
59
+ import { EventBus } from "@hashtagcms/helpers/event-bus";
60
+
61
+ // Send
62
+ EventBus.emit("my-event", { id: 1 });
63
+
64
+ // Listen
65
+ EventBus.on("my-event", (data) => {
66
+ console.log(data.id);
67
+ });
68
+ ```
69
+
70
+ ### `Common` Utilities
71
+
72
+ Exported from `@hashtagcms/helpers/common`:
73
+
74
+ - **`Toast`**: Displays global toast notifications.
75
+ - `Toast.show(vm, message, timeout, position)`
76
+ - **`Loader`**: Controls the global loading spinner.
77
+ - `Loader.show(vm, msg)`, `Loader.hide(vm)`
78
+ - **`Modal`**: Controls the global modal dialog.
79
+ - **`queryBuilder`**: Helper to parse URL query parameters.
80
+ - **`CopyToClipboard(text)`**: Async helper to copy text to clipboard.
81
+ - **`PasteFromClipboard()`**: Async helper to read from clipboard.
82
+
83
+ ## ⚠️ Dependencies
84
+
85
+ This package depends on:
86
+
87
+ - `axios`: For HTTP requests.
88
+ - `mitt`: For event handling.
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "@hashtagcms/helpers",
3
+ "version": "1.0.0",
4
+ "description": "Helper utilities for HashtagCMS",
5
+ "author": "HashtagCMS",
6
+ "license": "MIT",
7
+ "homepage": "https://hashtagcms.org",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/hashtagcms/admin-ui-kit.git"
11
+ },
12
+ "main": "src/common.js",
13
+ "exports": {
14
+ "./*": "./src/*.js"
15
+ },
16
+ "dependencies": {
17
+ "mitt": "^3.0.1",
18
+ "axios": "^1.8.4"
19
+ }
20
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Admin Configuration Helper
3
+ * Re-exporting from SDK
4
+ */
5
+ import { AdminConfig } from "@hashtagcms/admin-sdk";
6
+
7
+ const adminConfig = new AdminConfig();
8
+ export { AdminConfig };
9
+ export default adminConfig;
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Created by marghoob.suleman on 8/23/17.
3
+ */
4
+ import { EventBus } from "./event-bus";
5
+ import {
6
+ queryBuilder,
7
+ Storage,
8
+ Utils,
9
+ Fetcher,
10
+ CopyToClipboard,
11
+ PasteFromClipboard,
12
+ IsJson,
13
+ LeftMenu,
14
+ TitleCase
15
+ } from "@hashtagcms/admin-sdk";
16
+
17
+ // Initialize SDK LeftMenu with EventBus
18
+ if (LeftMenu && typeof LeftMenu.init === 'function') {
19
+ LeftMenu.init(EventBus);
20
+ }
21
+
22
+ export {
23
+ queryBuilder,
24
+ Storage,
25
+ Utils,
26
+ Fetcher,
27
+ CopyToClipboard,
28
+ PasteFromClipboard,
29
+ IsJson,
30
+ LeftMenu,
31
+ TitleCase
32
+ };
33
+
34
+ /**
35
+ * Toast
36
+ * Dependecy: library/toastBox.vue
37
+ *
38
+ */
39
+ export class Toast {
40
+ static show(vm, message = "", timeout = 1000, position) {
41
+ vm.$root.$refs.globalToaster.show(message, timeout, position);
42
+ }
43
+ static hide(vm) {
44
+ vm.$root.$refs.globalToaster.hide();
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Loader
50
+ * Dependecy: library/loader.vue
51
+ *
52
+ */
53
+ export class Loader {
54
+ static show(vm, message = null, position = null) {
55
+ vm.$root.$refs.globalLoader.show(message, position);
56
+ }
57
+ static hide(vm) {
58
+ vm.$root.$refs.globalLoader.hide();
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Modal
64
+ * Dependency: library/modalBox.vue
65
+ *
66
+ */
67
+ export class Modal {
68
+ static show(vm, message = "", position, callback, timeout) {
69
+ vm.$root.$refs.globalModalBox.show(message, position, callback, timeout);
70
+ }
71
+ static hide(vm) {
72
+ vm.$root.$refs.globalModalBox.hide();
73
+ }
74
+ static open(vm = null, modalRef = null, callback = null) {
75
+ if (vm != null && modalRef != null) {
76
+ vm.$refs[modalRef].open(callback);
77
+ }
78
+ }
79
+ static close(vm = null, modalRef = null) {
80
+ if (vm != null && modalRef != null) {
81
+ vm.$refs[modalRef].hide();
82
+ }
83
+ }
84
+ }
85
+
86
+
87
+
88
+
89
+
@@ -0,0 +1,16 @@
1
+ import { Dashboard } from "@hashtagcms/admin-sdk";
2
+
3
+ export const {
4
+ data,
5
+ labelsCategories,
6
+ datasCategories,
7
+ labelsContent,
8
+ datasContent,
9
+ bgColors,
10
+ borderColors,
11
+ createChart,
12
+ makeData,
13
+ init
14
+ } = Dashboard;
15
+
16
+ export { Dashboard };
@@ -0,0 +1,163 @@
1
+ import { AdminConfig, CleanForUrl } from "@hashtagcms/admin-sdk";
2
+
3
+ // Global AdminConfig instance wrapper if needed, or valid assuming window.AdminConfig is set.
4
+ // But better to use the class to be safe if window.AdminConfig isn't ready.
5
+ const adminConfig = new AdminConfig();
6
+
7
+ export const EditorHelper = (function () {
8
+ let makeRichEditor = (selector, settings) => {
9
+ selector = document.querySelector(selector);
10
+ let defaultSettings = {
11
+ selector: "#" + selector.id,
12
+ height: 500,
13
+ theme: "silver",
14
+ plugins:
15
+ "code print preview searchreplace autolink directionality visualblocks visualchars fullscreen image link media table charmap hr pagebreak nonbreaking anchor insertdatetime advlist lists wordcount imagetools textpattern",
16
+ toolbar1:
17
+ "formatselect | bold italic strikethrough forecolor backcolor | link | alignleft aligncenter alignright alignjustify | numlist bullist | removeformat | customGallery | image | code ",
18
+ image_advtab: true,
19
+ template_popup_height: 400,
20
+ template_popup_width: 320,
21
+ valid_elements: "*[*]",
22
+ valid_children: "*[*]",
23
+ document_base_url: adminConfig.get("app_url") + "/",
24
+ allow_script_urls: true,
25
+ convert_urls: false,
26
+ relative_urls: false,
27
+ remove_script_host: false,
28
+ content_css: ["//fonts.googleapis.com/css?family=Lato:300,300i,400,400i"],
29
+ setup: function (editor) {
30
+ editor.ui.registry.addButton("customGallery", {
31
+ text: "Insert Image",
32
+ onAction: function (_) {
33
+ if (Vue?.$refs?.imageGallery) {
34
+ Vue.$refs.imageGallery.open(editor);
35
+ }
36
+ },
37
+ });
38
+ },
39
+ };
40
+ defaultSettings = { ...defaultSettings, ...settings };
41
+ selector.editor = tinymce.init(defaultSettings);
42
+ };
43
+
44
+ return { makeRichEditor: makeRichEditor };
45
+ })();
46
+
47
+ /** Page Manager **/
48
+ export const PageManager = {
49
+ action: null,
50
+ content_type: null,
51
+ id: null,
52
+ init: function (action, content_type, id) {
53
+ this.action = action;
54
+ this.content_type = content_type;
55
+ this.id = id;
56
+
57
+ if (PageManager.action === "add") {
58
+ document
59
+ .getElementById("lang_name")
60
+ .addEventListener("change", PageManager.autoUpdateFields);
61
+ document
62
+ .getElementById("lang_title")
63
+ .addEventListener("change", PageManager.autoUpdateUrls);
64
+ document
65
+ .getElementById("link_rewrite")
66
+ .addEventListener("keyup", PageManager.linkRewriteUpdated);
67
+ document
68
+ .getElementById("category_id")
69
+ .addEventListener("change", PageManager.getParentCategory);
70
+ } else {
71
+ this.getParentCategory();
72
+ }
73
+ },
74
+ isBlank: function (elem) {
75
+ return document.getElementById(elem).value.replace(/\s/g, "") === "";
76
+ },
77
+
78
+ autoUpdateFields: function () {
79
+ let value = this.value;
80
+ try {
81
+ if (PageManager.isBlank("lang_title")) {
82
+ document.getElementById("lang_title").value =
83
+ value[0].toUpperCase() + value.slice(1);
84
+ document.getElementById("alias").value = CleanForUrl(
85
+ value.toUpperCase(),
86
+ "_",
87
+ );
88
+ let active_key = CleanForUrl(value.toLowerCase(), "-");
89
+ document.getElementById("lang_active_key").value = active_key;
90
+ document.getElementById("link_rewrite").value = active_key;
91
+ }
92
+ } catch (e) {
93
+ console.error(e.message);
94
+ }
95
+ },
96
+ autoUpdateUrls: function () {
97
+ let value = this.value;
98
+ if (document.getElementById("link_rewrite").edited !== true) {
99
+ value = CleanForUrl(value.toUpperCase(), "_");
100
+ value = value.substr(0, 60); //it's limit
101
+ document.getElementById("alias").value = value;
102
+
103
+ let active_key = CleanForUrl(value.toLowerCase(), "-");
104
+ active_key = value.substr(0, 128); //it's limit
105
+ document.getElementById("lang_active_key").value = active_key;
106
+ document.getElementById("link_rewrite").value = active_key;
107
+ }
108
+ },
109
+ linkRewriteUpdated: function () {
110
+ document.getElementById("link_rewrite").edited = true;
111
+ },
112
+ getParentCategory() {
113
+ let parentcombo = document.getElementById("parent_id");
114
+ let category_id = document.getElementById("category_id").value;
115
+ document.getElementById("parent_id").value = "";
116
+
117
+ parentcombo.length = 0;
118
+ parentcombo.options[0] = new Option("Select", "");
119
+
120
+ if (category_id > 0) {
121
+ showHideBlock(true);
122
+ let path = adminConfig.admin_path("page/getParentCategory", {
123
+ content_type: PageManager.content_type,
124
+ category_id: category_id,
125
+ });
126
+ axios
127
+ .get(path)
128
+ .then(function (res) {
129
+ //console.log(res);
130
+ updateCombo(res.data);
131
+ })
132
+ .catch(function (res) {
133
+ //console.log(res);
134
+ showHideBlock(false);
135
+ });
136
+ } else {
137
+ showHideBlock(false);
138
+ }
139
+
140
+ function updateCombo(res) {
141
+ if (res.length > 0) {
142
+ let index = 1;
143
+ for (let i = 0; i < res.length; i++) {
144
+ let current = res[i];
145
+ if (current.id !== PageManager.id) {
146
+ parentcombo.options[index] = new Option(
147
+ current.lang.name,
148
+ current.id,
149
+ );
150
+ index++;
151
+ }
152
+ }
153
+ } else {
154
+ showHideBlock(false);
155
+ }
156
+ }
157
+
158
+ function showHideBlock(show) {
159
+ document.getElementById("parent_div").style.display =
160
+ show === true ? "" : "none";
161
+ }
162
+ },
163
+ };
@@ -0,0 +1,50 @@
1
+ import { Toast } from "./common";
2
+
3
+ export class ErrorMessage {
4
+ constructor() {
5
+ this.errors = typeof error_messages == "undefined" ? [] : error_messages;
6
+ this.init();
7
+ }
8
+ init() {
9
+ let $this = this;
10
+ document.addEventListener("DOMContentLoaded", function () {
11
+ for (let i in $this.errors) {
12
+ $this.highlightField(i, $this.errors[i][0]);
13
+ }
14
+ $this.focusFirst();
15
+ });
16
+ }
17
+ highlightField(field, message) {
18
+ let f = field;
19
+ field = document.getElementById(f);
20
+
21
+ if (!field) {
22
+ console.error("Could not find " + f);
23
+ return false;
24
+ }
25
+ field.classList.add("is-invalid");
26
+
27
+ let div = document.createElement("div");
28
+ div.classList.add("alert", "alert-danger", "mt-03");
29
+
30
+ if (f.indexOf("lang_") !== -1) {
31
+ message = message.replace("lang ", "");
32
+ }
33
+ div.innerHTML = message;
34
+
35
+ field.parentNode.insertBefore(div, field.nextSibling);
36
+ }
37
+ focusFirst() {
38
+ for (let i in this.errors) {
39
+ let element = document.getElementById(i);
40
+ try {
41
+ if (element) {
42
+ element.focus();
43
+ break;
44
+ }
45
+ } catch (e) {
46
+ console.log(e.message);
47
+ }
48
+ }
49
+ }
50
+ }
@@ -0,0 +1,4 @@
1
+ // import Vue from 'vue';
2
+ // export const EventBus = new Vue();
3
+ import mitt from "mitt";
4
+ export const EventBus = new mitt();
@@ -0,0 +1,4 @@
1
+ import { Form, Errors } from "@hashtagcms/admin-sdk";
2
+
3
+ export { Errors };
4
+ export default Form;