@appscode/design-system 1.0.43-alpha.99 → 1.1.0-alpha.10

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 (103) hide show
  1. package/base/utilities/_all.scss +8 -0
  2. package/base/utilities/_customize-bulma.scss +191 -0
  3. package/base/utilities/_default.scss +58 -124
  4. package/base/utilities/_derived-variables.scss +6 -0
  5. package/base/utilities/_extended.scss +38 -0
  6. package/base/utilities/_grid.scss +29 -0
  7. package/base/utilities/_initial-variables.scss +14 -10
  8. package/base/utilities/_typography.scss +6 -12
  9. package/base/utilities/dark-theme.scss +1 -0
  10. package/components/_ac-accordion.scss +14 -5
  11. package/components/_ac-alert-box.scss +32 -6
  12. package/components/_ac-card.scss +17 -5
  13. package/components/_ac-drag.scss +2 -0
  14. package/components/_ac-input.scss +19 -11
  15. package/components/_ac-modal.scss +1 -1
  16. package/components/_ac-multi-select.scss +60 -4
  17. package/components/_ac-report.scss +53 -0
  18. package/components/_ac-table.scss +60 -2
  19. package/components/_ac-tabs.scss +16 -2
  20. package/components/_ac-tags.scss +85 -0
  21. package/components/_ac-terminal.scss +1 -3
  22. package/components/_all.scss +29 -0
  23. package/components/_basic-card.scss +128 -0
  24. package/components/_buttons.scss +14 -33
  25. package/components/_dashboard-header.scss +32 -0
  26. package/components/_left-sidebar-menu.scss +9 -9
  27. package/components/_navbar.scss +89 -4
  28. package/components/_preview-modal.scss +14 -1
  29. package/components/_transitions.scss +296 -0
  30. package/components/_wizard.scss +1 -0
  31. package/components/bbum/_all.scss +9 -0
  32. package/components/bbum/_single-post-preview.scss +1 -1
  33. package/components/ui-builder/_ui-builder.scss +65 -1
  34. package/components/ui-builder/_vue-open-api.scss +6 -0
  35. package/layouts/_all.scss +2 -0
  36. package/layouts/_code-preview.scss +5 -2
  37. package/main.scss +5 -56
  38. package/package.json +4 -2
  39. package/plugins/caching.ts +243 -0
  40. package/plugins/time-convert.js +49 -0
  41. package/plugins/vue-toaster.js +3 -0
  42. package/vue-components/v2/banner/Banner.vue +2 -2
  43. package/vue-components/v2/breadcrumbs/Breadcrumb.vue +97 -0
  44. package/vue-components/v2/button/Button.vue +5 -0
  45. package/vue-components/v2/button/DownloadBtn.vue +45 -0
  46. package/vue-components/v2/card/Card.vue +1 -0
  47. package/vue-components/v2/content/ContentTable.vue +10 -0
  48. package/vue-components/v2/editor/Editor.vue +37 -24
  49. package/vue-components/v2/editor/FilteredFileEditor.vue +189 -0
  50. package/vue-components/v2/editor/MonacoEditor.vue +125 -0
  51. package/vue-components/v2/editor/ResourceKeyValueEditor.vue +209 -0
  52. package/vue-components/v2/form-fields/Input.vue +1 -1
  53. package/vue-components/v2/loaders/ResourceLoader.vue +101 -0
  54. package/vue-components/v2/loaders/SidebarLoader.vue +43 -0
  55. package/vue-components/v2/modal/Modal.vue +31 -5
  56. package/vue-components/v2/modals/DeleteConfirmationModal.vue +79 -0
  57. package/vue-components/v2/modals/JsonShowModal.vue +12 -2
  58. package/vue-components/v2/navbar/User.vue +229 -17
  59. package/vue-components/v2/notification/Notification.vue +101 -0
  60. package/vue-components/v2/notification/NotificationItem.vue +44 -0
  61. package/vue-components/v2/pagination/Pagination.vue +16 -3
  62. package/vue-components/v2/preloader/Preloader.vue +1 -1
  63. package/vue-components/v2/sidebar/SidebarItemWithDropDown.vue +19 -20
  64. package/vue-components/v2/tab/TabItem.vue +1 -1
  65. package/vue-components/v2/table/Table.vue +49 -8
  66. package/vue-components/v2/table/TableRow.vue +12 -2
  67. package/vue-components/v2/table/table-cell/CellValue.vue +29 -9
  68. package/vue-components/v2/table/table-cell/GenericCell.vue +56 -0
  69. package/vue-components/v2/table/table-cell/ObjectCell.vue +4 -1
  70. package/vue-components/v3/button/Button.vue +6 -1
  71. package/vue-components/v3/content/ContentHeader.vue +2 -1
  72. package/vue-components/v3/content/ContentTable.vue +25 -2
  73. package/vue-components/v3/editor/Editor.vue +36 -33
  74. package/vue-components/v3/editor/FilteredFileEditor.vue +186 -0
  75. package/vue-components/v3/editor/MonacoEditor.vue +131 -0
  76. package/vue-components/v3/editor/ResourceKeyValueEditor.vue +125 -0
  77. package/vue-components/v3/form/Form.vue +63 -0
  78. package/vue-components/v3/form-fields/Input.vue +11 -10
  79. package/vue-components/v3/header/HeaderItem.vue +5 -0
  80. package/vue-components/v3/header/HeaderItems.vue +5 -0
  81. package/vue-components/v3/loaders/ResourceLoader.vue +83 -0
  82. package/vue-components/v3/loaders/SidebarLoader.vue +34 -0
  83. package/vue-components/v3/long-running-tasks/LongRunningTaskItem.vue +92 -0
  84. package/vue-components/v3/modal/Modal.vue +35 -7
  85. package/vue-components/v3/modals/DeleteConfirmationModal.vue +85 -0
  86. package/vue-components/v3/modals/JsonShowModal.vue +25 -16
  87. package/vue-components/v3/modals/LongRunningTasksModal.vue +400 -0
  88. package/vue-components/v3/navbar/ThemeMode.vue +41 -49
  89. package/vue-components/v3/navbar/User.vue +242 -18
  90. package/vue-components/v3/notification/AlertBox.vue +61 -0
  91. package/vue-components/v3/notification/Notification.vue +98 -0
  92. package/vue-components/v3/notification/NotificationItem.vue +52 -0
  93. package/vue-components/v3/pagination/Pagination.vue +16 -3
  94. package/vue-components/v3/sidebar/SidebarItemWithDropDown.vue +120 -0
  95. package/vue-components/v3/tab/TabItem.vue +1 -1
  96. package/vue-components/v3/table/MultiInfoTable.vue +143 -0
  97. package/vue-components/v3/table/Table.vue +55 -14
  98. package/vue-components/v3/table/TableContainer.vue +34 -0
  99. package/vue-components/v3/table/TableRow.vue +93 -6
  100. package/vue-components/v3/table/table-cell/CellValue.vue +23 -7
  101. package/vue-components/v3/table/table-cell/GenericCell.vue +75 -0
  102. package/vue-components/v3/table/table-cell/ObjectCell.vue +7 -2
  103. package/vue-components/v3/terminal/LongRunningTaskTerminal.vue +148 -0
@@ -0,0 +1,243 @@
1
+ import { AxiosRequestConfig, AxiosResponse } from "axios";
2
+ import md5 from "crypto-js/md5";
3
+ import stringify from "fast-json-stable-stringify";
4
+
5
+ const resourceListApiRgx =
6
+ /^\/clusters\/([^/]+)\/([^/]+)\/proxy\/([^/]+)\/([^/]+)(\/namespaces\/([^/?]+))?\/([^/?]+)(.*)$/;
7
+ const usermenuUpdateApiRgx =
8
+ /^\/clusters\/([^/]+)\/([^/]+)\/proxy\/meta\.k8s\.appscode\.com\/([^/]+)\/usermenus\/cluster$/;
9
+
10
+ function getRequestInterceptor(config: AxiosRequestConfig) {
11
+ // get call
12
+ const { url, _recurringCall, params } = config;
13
+ const matchListApi = url?.match(resourceListApiRgx);
14
+ if (matchListApi) {
15
+ // url matches list / render api call
16
+ const [, user, cluster, group, version, , namespace, resource] =
17
+ matchListApi;
18
+
19
+ const suffix = `${user}.${cluster}.${group}.${version}.${resource}${
20
+ namespace ? ".ns." + namespace : ""
21
+ }${params ? ".q." + md5(stringify(params)).toString() : ""}`;
22
+
23
+ if (_recurringCall) {
24
+ // always send latest date time as ctag
25
+ return new Date().getTime();
26
+ } else {
27
+ // read ctag from local storage if exists
28
+ const storageKey = `ctag.${suffix}`;
29
+ // read local storage ctag
30
+ return localStorage.getItem(storageKey);
31
+ }
32
+ }
33
+ }
34
+
35
+ function getResponseInterceptor(resp: AxiosResponse) {
36
+ // get call
37
+ const { config, headers } = resp;
38
+ const { url, _recurringCall, params } = config;
39
+
40
+ // queryParams without ctag
41
+ const queryParamsWithoutCtag = Object.keys(params).reduce((ob, key) => {
42
+ if (key !== "ctag") return { ...ob, [key]: params[key] };
43
+ else return { ...ob };
44
+ }, {});
45
+
46
+ const matchListApi = url?.match(resourceListApiRgx);
47
+ if (matchListApi) {
48
+ // url matches list / render api call
49
+ // window.console.log(matchListApi)
50
+ const [, user, cluster, group, version, , namespace, resource] =
51
+ matchListApi;
52
+
53
+ const suffix = `${user}.${cluster}.${group}.${version}.${resource}${
54
+ namespace ? ".ns." + namespace : ""
55
+ }${
56
+ params ? ".q." + md5(stringify(queryParamsWithoutCtag)).toString() : ""
57
+ }`;
58
+
59
+ const storageKey = `etag.${suffix}`;
60
+ // local storage etag
61
+ const eTag = localStorage.getItem(storageKey);
62
+ // etag in header
63
+ const headerEtag = headers?.["etag"];
64
+
65
+ // window.console.log({ eTag, headerEtag, _recurringCall, url });
66
+
67
+ if (_recurringCall) {
68
+ // for recurring api call
69
+ if (headerEtag && headerEtag !== eTag) {
70
+ // etag missmatch
71
+ if (params.ctag) {
72
+ // save request query parameter ctag into local storage
73
+ localStorage.setItem(`ctag.${suffix}`, params.ctag);
74
+ }
75
+ // update store etag with latest header etag
76
+ localStorage.setItem(`etag.${suffix}`, headerEtag);
77
+
78
+ if (namespace) {
79
+ // for namespaced list calls if etag missmatch happens
80
+ // update the ctag storage for all namespaces
81
+ // this will force the all namespaced list call to ignore disk cache
82
+ localStorage.setItem(
83
+ `etag.${user}.${cluster}.${group}.${version}.${resource}`,
84
+ new Date().getTime().toString()
85
+ );
86
+ }
87
+ }
88
+ } else {
89
+ // not recurring api call
90
+ if (eTag !== headerEtag && headerEtag) {
91
+ // if the etag missmatches, then the data has changed
92
+ // update local etag
93
+ localStorage.setItem(storageKey, headerEtag);
94
+ }
95
+ }
96
+ }
97
+ }
98
+
99
+ function putResponseInterceptor(resp: AxiosResponse) {
100
+ const { config } = resp;
101
+ const { url } = config;
102
+ const matchUsermenuUpdate = url?.match(usermenuUpdateApiRgx);
103
+ if (matchUsermenuUpdate) {
104
+ // user menu update api is called
105
+ const [, user, cluster, version] = matchUsermenuUpdate;
106
+
107
+ // calculate the local storage key
108
+ const renderStorageKey = `ctag.${user}.${cluster}.meta.k8s.appscode.com.${version}.rendermenus.q.${md5(
109
+ stringify({
110
+ apiVersion: `meta.k8s.appscode.com/${version}`,
111
+ kind: "RenderMenu",
112
+ request: {
113
+ menu: "cluster",
114
+ mode: "Accordion",
115
+ },
116
+ })
117
+ ).toString()}`;
118
+ const availableMenuStorageKey = `ctag.${user}.${cluster}.meta.k8s.appscode.com.${version}.usermenus`;
119
+
120
+ // set new ctag to that storage key
121
+ // next time render menu is called, cached data won't be used
122
+ localStorage.setItem(renderStorageKey, new Date().getTime().toString());
123
+ // for all available menus api
124
+ localStorage.setItem(
125
+ availableMenuStorageKey,
126
+ new Date().getTime().toString()
127
+ );
128
+ }
129
+ }
130
+
131
+ export function handleCacheFromYamls(
132
+ yamls: Array<{
133
+ apiVersion: string;
134
+ kind: string;
135
+ metadata: { name: string; namespace: string };
136
+ }>,
137
+ user: string,
138
+ cluster: string,
139
+ gkToGvrMap: Record<string, string>
140
+ ) {
141
+ return function () {
142
+ yamls.forEach((yaml) => {
143
+ const { apiVersion, kind, metadata } = yaml;
144
+ const { namespace } = metadata || {};
145
+ const apiVersionMatch = apiVersion.match(/^(([^/]+)\/)?([^/]+)$/);
146
+ if (apiVersionMatch) {
147
+ // separate the group and version
148
+ const [, , group] = apiVersionMatch;
149
+ const groupKind = `${group || "core"}/${kind}`;
150
+ const gvr = (gkToGvrMap && gkToGvrMap[groupKind]) || "";
151
+
152
+ const storageKeyPrefix = `ctag.${user}.${cluster}.${gvr.replaceAll(
153
+ "/",
154
+ "."
155
+ )}`;
156
+
157
+ // clear list cache for all namespaces
158
+ localStorage.setItem(
159
+ `${storageKeyPrefix}.q.${md5(
160
+ stringify({ convertToTable: true })
161
+ ).toString()}`,
162
+ new Date().getTime().toString()
163
+ );
164
+ // clear list cache for specific namespace
165
+ localStorage.setItem(
166
+ `${storageKeyPrefix}.ns.${namespace || "default"}.q.${md5(
167
+ stringify({ convertToTable: true })
168
+ ).toString()}`,
169
+ new Date().getTime().toString()
170
+ );
171
+
172
+ // clear for genericresources and resourcesummaries
173
+ localStorage.setItem(
174
+ `ctag.${user}.${cluster}.core.k8s.appscode.com.v1alpha1.resourcesummaries.q.${md5(
175
+ stringify({
176
+ convertToTable: true,
177
+ labelSelector: `k8s.io/group=${group}`,
178
+ })
179
+ ).toString()}`,
180
+ new Date().getTime().toString()
181
+ );
182
+ localStorage.setItem(
183
+ `ctag.${user}.${cluster}.core.k8s.appscode.com.v1alpha1.resourcesummaries.q.${md5(
184
+ stringify({
185
+ convertToTable: true,
186
+ labelSelector: `k8s.io/group-kind=${kind}.${group}`,
187
+ })
188
+ ).toString()}`,
189
+ new Date().getTime().toString()
190
+ );
191
+ localStorage.setItem(
192
+ `ctag.${user}.${cluster}.core.k8s.appscode.com.v1alpha1.genericresources.q.${md5(
193
+ stringify({
194
+ convertToTable: true,
195
+ labelSelector: `k8s.io/group=${group}`,
196
+ })
197
+ ).toString()}`,
198
+ new Date().getTime().toString()
199
+ );
200
+ localStorage.setItem(
201
+ `ctag.${user}.${cluster}.core.k8s.appscode.com.v1alpha1.genericresources.q.${md5(
202
+ stringify({
203
+ convertToTable: true,
204
+ labelSelector: `k8s.io/group-kind=${kind}.${group}`,
205
+ })
206
+ ).toString()}`,
207
+ new Date().getTime().toString()
208
+ );
209
+ }
210
+ });
211
+ };
212
+ }
213
+
214
+ export async function requestInterceptor(config: AxiosRequestConfig) {
215
+ // parse the request url
216
+ const { method, params } = config;
217
+
218
+ let ctag;
219
+
220
+ if (method === "get") {
221
+ ctag = getRequestInterceptor(config);
222
+ }
223
+
224
+ return {
225
+ ...config,
226
+ params: { ...params, ctag },
227
+ };
228
+ }
229
+
230
+ export function responseInterceptor(resp: AxiosResponse) {
231
+ const { config } = resp;
232
+ // parse the request url
233
+ const { method } = config;
234
+
235
+ if (method === "get") {
236
+ // get call
237
+ getResponseInterceptor(resp);
238
+ } else if (method === "put") {
239
+ putResponseInterceptor(resp);
240
+ }
241
+
242
+ return resp;
243
+ }
@@ -0,0 +1,49 @@
1
+ import moment from "moment";
2
+ import { useNow, useThrottleFn } from "@vueuse/core";
3
+
4
+ const getTime = option => {
5
+ if (parseInt(option.time, 10) < 0 || !option.time) {
6
+ return undefined;
7
+ }
8
+ let time = option.time;
9
+
10
+ // moment(option.time).valueOf('x') needs to convert pharmer's api date to epoch time
11
+ time = moment(option.time).valueOf("x")
12
+ ? moment(option.time).valueOf("x")
13
+ : time * 1000;
14
+
15
+ return moment(time).format("MMM DD YYYY, h:mm A");
16
+ };
17
+ const getDayDifferences = options => {
18
+ const past = moment(options.past).isValid()
19
+ ? moment(options.past).valueOf("x") / 1000
20
+ : options.past;
21
+ const now = Date.now() / 1000;
22
+ const diff = now - past;
23
+ if (parseInt(options.past, 10) > 10) {
24
+ let ret = Math.floor(diff / 86400);
25
+ let unit = "";
26
+ if (diff < 60) {
27
+ ret = parseInt(diff, 10);
28
+ unit = " Second";
29
+ } else if (diff < 3600) {
30
+ ret = parseInt(diff / 60, 10);
31
+ unit = " Minute";
32
+ } else if (diff < 86400) {
33
+ ret = parseInt(diff / 3600, 10);
34
+ unit = " Hour";
35
+ } else {
36
+ ret = parseInt(diff / 86400, 10);
37
+ unit = " Day";
38
+ }
39
+ unit += ret > 1 ? "s" : "";
40
+ return ret + unit;
41
+ }
42
+ return undefined;
43
+ };
44
+
45
+ export default {
46
+ getTime,
47
+ // formatMoment,
48
+ getDayDifferences
49
+ };
@@ -41,6 +41,7 @@ module.exports = {
41
41
  },
42
42
  options: {
43
43
  icon: "fa-warning",
44
+ duration: 20000,
44
45
  type: "error",
45
46
  className: "ac-toast is-error",
46
47
  },
@@ -56,6 +57,7 @@ module.exports = {
56
57
  },
57
58
  options: {
58
59
  icon: "fa-info",
60
+ duration: 20000,
59
61
  type: "info",
60
62
  className: "ac-toast is-info",
61
63
  },
@@ -73,6 +75,7 @@ module.exports = {
73
75
  },
74
76
  options: {
75
77
  icon: "fa-warning",
78
+ duration: 2500,
76
79
  type: "error",
77
80
  className: "ac-toast is-warning",
78
81
  },
@@ -1,8 +1,8 @@
1
1
  <template>
2
2
  <div class="sign-up-notification mb-20">
3
3
  <div class="notification-inner has-text-centered">
4
- <h3><slot name="banner-title" /></h3>
5
- <slot />
4
+ <h3><slot name="banner-title">Error!</slot></h3>
5
+ <slot><p>Oops!! There was an error while loading!</p></slot>
6
6
  </div>
7
7
  </div>
8
8
  </template>
@@ -0,0 +1,97 @@
1
+ <template>
2
+ <div class="ac-breadcrumb">
3
+ <nav aria-label="breadcrumbs" class="breadcrumb">
4
+ <ul>
5
+ <li
6
+ v-for="(item, idx) in list"
7
+ :key="idx"
8
+ :class="{ 'is-active': idx === list.length - 1 }"
9
+ >
10
+ <router-link
11
+ class="router-link-active"
12
+ :class="{ 'is-active': idx === list.length - 1 }"
13
+ :to="item.path"
14
+ >{{ item.name }}
15
+ </router-link>
16
+ </li>
17
+ </ul>
18
+ </nav>
19
+ </div>
20
+ </template>
21
+ <script>
22
+ export default {
23
+ data() {
24
+ return {
25
+ list: []
26
+ };
27
+ },
28
+ methods: {
29
+ createBreadcrumbs(n) {
30
+ //Adding dynamic path to the route
31
+ const listPaths = this.$route.matched.map(element =>
32
+ this.pathReplaceWithParam(element.path)
33
+ );
34
+
35
+ //Removing the last duplicate value from the listpaths array
36
+ listPaths.pop();
37
+
38
+ //Createing the breadcrumb name
39
+ const listName = this.createList(listPaths);
40
+
41
+ //Set the new breadcrumb name and path value to list
42
+ this.list = listName.map((element, index) => {
43
+ return {
44
+ name: element,
45
+ path: listPaths[index]
46
+ };
47
+ });
48
+ },
49
+ pathReplaceWithParam(path) {
50
+ //Split the path and remove the first and last empty block
51
+ let splitPath = path.split("/").filter(element => element !== "");
52
+
53
+ //Replace all element with query params where the element start with ":"
54
+ // Then again add them wthi "/" and return the path value
55
+ return splitPath.reduce((pval, cval) => {
56
+ if (cval[0] === ":") {
57
+ const cutIndex =
58
+ cval.indexOf("?") !== -1 ? cval.indexOf("?") : cval.length;
59
+ return (pval += this.$route.params[cval.slice(1, cutIndex)] + "/");
60
+ } else return (pval += cval + "/");
61
+ }, "/");
62
+ },
63
+ createList(paths) {
64
+ //Split all the path and remove all empty block
65
+ let spath = paths.map(element => {
66
+ return element.split("/").filter(word => word !== "");
67
+ });
68
+
69
+ // Convert all the path arry in one arry where each element is the different fo previous element
70
+ return spath.reduce((prev, curr, currentIdx) => {
71
+ if (currentIdx === 0) return prev.concat([this.createVal(0, curr)]);
72
+ else
73
+ return prev.concat([
74
+ this.createVal(spath[currentIdx - 1].length, curr)
75
+ ]);
76
+ }, []);
77
+ },
78
+ createVal(startIdx, paths) {
79
+ return paths.reduce((prev, curr, idx) => {
80
+ if (idx >= startIdx) {
81
+ if (idx + 1 === paths.length) return (prev += curr);
82
+ else return (prev += curr + " / ");
83
+ } else return prev + "";
84
+ }, "");
85
+ }
86
+ },
87
+ watch: {
88
+ "$route.path": {
89
+ deep: true,
90
+ immediate: true,
91
+ handler(n) {
92
+ this.createBreadcrumbs(n);
93
+ }
94
+ }
95
+ }
96
+ };
97
+ </script>
@@ -4,6 +4,7 @@
4
4
  :class="`${modifierClasses}${isLoaderActive ? ' is-loading' : ''}`"
5
5
  :disabled="disabled"
6
6
  @click="handleClick"
7
+ :title="tooltip"
7
8
  >
8
9
  <span v-if="iconClass || iconImage" class="icon is-small">
9
10
  <img
@@ -27,6 +28,10 @@ export default {
27
28
  type: String,
28
29
  default: "",
29
30
  },
31
+ tooltip: {
32
+ type: String,
33
+ defualt: undefined
34
+ },
30
35
  // for loader
31
36
  isLoaderActive: {
32
37
  type: Boolean,
@@ -0,0 +1,45 @@
1
+ <template>
2
+ <ac-button
3
+ modifier-classes="is-primary is-square"
4
+ icon-class="download"
5
+ :is-loader-active="isFetching"
6
+ @click="download()"
7
+ />
8
+ </template>
9
+
10
+ <script>
11
+ import downloadFunc from "downloadjs";
12
+
13
+ export default {
14
+ components: {
15
+ AcButton: () => import("./../button/Button.vue")
16
+ },
17
+ props: {
18
+ fileData: {
19
+ type: String,
20
+ default: ""
21
+ },
22
+ fileName: {
23
+ type: String,
24
+ default: ""
25
+ },
26
+ isFetching: {
27
+ type: Boolean,
28
+ default: false
29
+ }
30
+ },
31
+ data() {
32
+ return {
33
+ downloadIcon: "fa fa-cloud-download"
34
+ };
35
+ },
36
+ component: {
37
+ downloadFunc
38
+ },
39
+ methods: {
40
+ download() {
41
+ downloadFunc(this.fileData, this.fileName);
42
+ }
43
+ }
44
+ };
45
+ </script>
@@ -3,6 +3,7 @@
3
3
  class="ac-single-card has-text-centered style-three"
4
4
  :class="{ 'is-selected': isSelected }"
5
5
  @click="$emit('click', $event)"
6
+ data-testid="card-list-item"
6
7
  >
7
8
  <div class="ac-card-logo" v-if="hasLogo">
8
9
  <slot name="card-logo" />
@@ -2,8 +2,10 @@
2
2
  <content-layout>
3
3
  <template #content-header>
4
4
  <content-header
5
+ v-if="!hideHeader"
5
6
  :header-title="tableTitle"
6
7
  :header-sub-title="tableSubTitle"
8
+ :remove-border-bottom="removeBorderBottom"
7
9
  :class="{ 'pl-0 pr-0': removeTableHeaderPadding }"
8
10
  >
9
11
  <header-item>
@@ -35,6 +37,14 @@ export default {
35
37
  type: Boolean,
36
38
  default: true,
37
39
  },
40
+ hideHeader: {
41
+ type: Boolean,
42
+ default: false,
43
+ },
44
+ removeBorderBottom: {
45
+ type: Boolean,
46
+ default: false,
47
+ },
38
48
  },
39
49
  components: {
40
50
  ContentLayout: () => import("./ContentLayout.vue"),
@@ -7,21 +7,22 @@
7
7
  />
8
8
  <monaco-editor
9
9
  v-if="activeTab === 'edit'"
10
- ref="monacoEditor"
11
10
  @editorDidMount="onEditorMount"
12
11
  key="edit"
13
12
  :class="`vh-${editorHeight} is-clipped`"
14
- v-model="editorContent"
13
+ :value="editorContent"
14
+ @change="onChange"
15
15
  :language="language"
16
16
  :options="{
17
17
  minimap: {
18
- enabled: calcShowMinimap
18
+ enabled: calcShowMinimap,
19
19
  },
20
20
  theme: theme,
21
21
  readOnly: readOnly,
22
22
  wordWrap: wordWrap,
23
- scrollBeyondLastLine: false
23
+ scrollBeyondLastLine: false,
24
24
  }"
25
+ data-testid="monaco-editor-edit-section"
25
26
  />
26
27
  <monaco-editor
27
28
  v-if="activeTab === 'preview'"
@@ -31,65 +32,75 @@
31
32
  :language="language"
32
33
  :options="{
33
34
  minimap: {
34
- enabled: calcShowMinimap
35
+ enabled: calcShowMinimap,
35
36
  },
36
37
  theme: theme,
37
38
  readOnly: true,
38
39
  wordWrap: wordWrap,
39
- scrollBeyondLastLine: false
40
+ scrollBeyondLastLine: false,
40
41
  }"
41
42
  :original="originalEditorContent"
42
43
  :diff-editor="true"
44
+ data-testid="monaco-editor-preview-section"
43
45
  />
44
46
  </div>
45
47
  </template>
46
48
 
47
49
  <script>
50
+ import Preloader from "../preloader/Preloader.vue";
51
+ import Banner from "../banner/Banner.vue";
48
52
  export default {
49
53
  props: {
50
54
  value: {
51
55
  type: String,
52
- default: ""
56
+ default: "",
53
57
  },
54
58
  originalValue: {
55
59
  type: String,
56
- default: ""
60
+ default: "",
57
61
  },
58
62
  readOnly: {
59
63
  type: Boolean,
60
- default: false
64
+ default: false,
61
65
  },
62
66
  language: {
63
67
  type: String,
64
- default: "yaml"
68
+ default: "yaml",
65
69
  },
66
70
  showMinimap: {
67
71
  type: Boolean,
68
- default: true
72
+ default: true,
69
73
  },
70
74
  editorHeight: {
71
75
  type: Number,
72
- default: 40
76
+ default: 40,
73
77
  },
74
78
  editorTheme: {
75
79
  type: String,
76
- default: ""
80
+ default: "",
77
81
  },
78
82
  wordWrap: {
79
83
  type: String,
80
- default: "off"
84
+ default: "off",
81
85
  },
82
86
  },
87
+
83
88
  components: {
84
89
  EditorTabs: () => import("../tabs/EditorTabs.vue"),
85
- MonacoEditor: () => import("vue-monaco")
90
+ MonacoEditor: () => ({
91
+ component: import("./MonacoEditor.vue"),
92
+ loading: Preloader,
93
+ delay: 200,
94
+ error: Banner,
95
+ timeout: 100000,
96
+ }),
86
97
  },
87
98
 
88
99
  data() {
89
100
  return {
90
101
  activeTab: "edit",
91
102
  editorContent: "",
92
- originalEditorContent: ""
103
+ originalEditorContent: "",
93
104
  };
94
105
  },
95
106
 
@@ -105,7 +116,7 @@ export default {
105
116
  ? "vs-dark"
106
117
  : "vs")
107
118
  );
108
- }
119
+ },
109
120
  },
110
121
 
111
122
  watch: {
@@ -115,7 +126,7 @@ export default {
115
126
  if (this.editorContent !== n) {
116
127
  this.editorContent = n;
117
128
  }
118
- }
129
+ },
119
130
  },
120
131
  originalValue: {
121
132
  immediate: true,
@@ -123,18 +134,20 @@ export default {
123
134
  if (this.originalEditorContent !== n) {
124
135
  this.originalEditorContent = n;
125
136
  }
126
- }
127
- }
137
+ },
138
+ },
128
139
  },
129
140
 
130
141
  methods: {
131
- onEditorMount() {
132
- const editor = this.$refs.monacoEditor.getEditor();
142
+ onChange(e) {
143
+ if (typeof e === "string") this.editorContent = e;
144
+ },
145
+ onEditorMount(editor) {
133
146
  // add event listeners
134
147
  editor.onDidBlurEditorText(() => {
135
148
  this.$emit("input", this.editorContent);
136
149
  });
137
- }
138
- }
150
+ },
151
+ },
139
152
  };
140
153
  </script>