@adminforth/dashboard 1.6.0 → 1.7.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.
@@ -5,6 +5,7 @@
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "dependencies": {
8
+ "monaco-editor": "^0.55.1",
8
9
  "yaml": "^2.9.0"
9
10
  }
10
11
  }
@@ -8,12 +8,29 @@ importers:
8
8
 
9
9
  .:
10
10
  dependencies:
11
+ monaco-editor:
12
+ specifier: ^0.55.1
13
+ version: 0.55.1
11
14
  yaml:
12
15
  specifier: ^2.9.0
13
16
  version: 2.9.0
14
17
 
15
18
  packages:
16
19
 
20
+ '@types/trusted-types@2.0.7':
21
+ resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
22
+
23
+ dompurify@3.2.7:
24
+ resolution: {integrity: sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw==}
25
+
26
+ marked@14.0.0:
27
+ resolution: {integrity: sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==}
28
+ engines: {node: '>= 18'}
29
+ hasBin: true
30
+
31
+ monaco-editor@0.55.1:
32
+ resolution: {integrity: sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A==}
33
+
17
34
  yaml@2.9.0:
18
35
  resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==}
19
36
  engines: {node: '>= 14.6'}
@@ -21,4 +38,18 @@ packages:
21
38
 
22
39
  snapshots:
23
40
 
41
+ '@types/trusted-types@2.0.7':
42
+ optional: true
43
+
44
+ dompurify@3.2.7:
45
+ optionalDependencies:
46
+ '@types/trusted-types': 2.0.7
47
+
48
+ marked@14.0.0: {}
49
+
50
+ monaco-editor@0.55.1:
51
+ dependencies:
52
+ dompurify: 3.2.7
53
+ marked: 14.0.0
54
+
24
55
  yaml@2.9.0: {}
@@ -54,7 +54,7 @@
54
54
  class="fixed inset-0 z-50 flex items-center justify-center bg-black/35 p-4"
55
55
  @click.self="closeGroupConfigEditor"
56
56
  >
57
- <section class="w-full max-w-2xl rounded-lg border border-lightListBorder bg-lightDropdownOptionsBackground p-4 shadow-xl dark:border-darkListBorder dark:bg-darkDropdownOptionsBackground">
57
+ <section class="w-full max-w-5xl rounded-lg border border-lightListBorder bg-lightDropdownOptionsBackground p-4 shadow-xl dark:border-darkListBorder dark:bg-darkDropdownOptionsBackground">
58
58
  <header class="mb-3 flex items-center justify-between gap-3">
59
59
  <h2 class="m-0 text-base font-bold text-lightNavbarText dark:text-darkNavbarText">
60
60
  Group JSON
@@ -81,12 +81,9 @@
81
81
  </button>
82
82
  </header>
83
83
 
84
- <textarea
84
+ <YamlConfigEditor
85
85
  v-model="groupConfigCode"
86
- class="min-h-[500px] w-full resize-y rounded-lg border border-lightListBorder bg-lightListTable p-3 font-mono text-sm text-lightNavbarText outline-none focus:border-lightPrimaryButtonBackground dark:border-darkListBorder dark:bg-darkListTable dark:text-darkNavbarText dark:focus:border-darkPrimaryButtonBackground"
87
- spellcheck="false"
88
- @keydown.ctrl.enter.prevent="saveGroupConfig"
89
- @keydown.meta.enter.prevent="saveGroupConfig"
86
+ @save="saveGroupConfig"
90
87
  />
91
88
 
92
89
  <div
@@ -120,7 +117,7 @@
120
117
  class="fixed inset-0 z-50 flex items-center justify-center bg-black/35 p-4"
121
118
  @click.self="closeWidgetConfigEditor"
122
119
  >
123
- <section class="w-full max-w-2xl rounded-lg border border-lightListBorder bg-lightDropdownOptionsBackground p-4 shadow-xl dark:border-darkListBorder dark:bg-darkDropdownOptionsBackground">
120
+ <section class="w-full max-w-5xl rounded-lg border border-lightListBorder bg-lightDropdownOptionsBackground p-4 shadow-xl dark:border-darkListBorder dark:bg-darkDropdownOptionsBackground">
124
121
  <header class="mb-3 flex items-center justify-between gap-3">
125
122
  <h2 class="m-0 text-base font-bold text-lightNavbarText dark:text-darkNavbarText">
126
123
  Widget JSON
@@ -147,12 +144,9 @@
147
144
  </button>
148
145
  </header>
149
146
 
150
- <textarea
147
+ <YamlConfigEditor
151
148
  v-model="widgetConfigCode"
152
- class="min-h-[500px] w-full resize-y rounded-lg border border-lightListBorder bg-lightListTable p-3 font-mono text-sm text-lightNavbarText outline-none focus:border-lightPrimaryButtonBackground dark:border-darkListBorder dark:bg-darkListTable dark:text-darkNavbarText dark:focus:border-darkPrimaryButtonBackground"
153
- spellcheck="false"
154
- @keydown.ctrl.enter.prevent="saveWidgetConfig"
155
- @keydown.meta.enter.prevent="saveWidgetConfig"
149
+ @save="saveWidgetConfig"
156
150
  />
157
151
 
158
152
  <div
@@ -203,6 +197,7 @@ import { computed, ref, watch } from 'vue'
203
197
  import { parse as parseYaml, stringify as stringifyYaml } from 'yaml'
204
198
  import { Button } from '@/afcl'
205
199
  import DashboardGroup from './DashboardGroup.vue'
200
+ import YamlConfigEditor from './YamlConfigEditor.vue'
206
201
  import { DashboardApiError, dashboardApi, type DashboardResponse } from '../api/dashboardApi.js'
207
202
  import type {
208
203
  DashboardConfig,
@@ -0,0 +1,109 @@
1
+ <template>
2
+ <div
3
+ ref="editorEl"
4
+ class="min-h-[500px] w-full overflow-hidden rounded-lg border border-lightListBorder bg-lightListTable text-sm dark:border-darkListBorder dark:bg-darkListTable"
5
+ />
6
+ </template>
7
+
8
+ <script setup lang="ts">
9
+ import 'monaco-editor/min/vs/editor/editor.main.css'
10
+ import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker'
11
+ import { nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
12
+ import type * as Monaco from 'monaco-editor'
13
+
14
+ declare global {
15
+ interface Window {
16
+ MonacoEnvironment?: {
17
+ getWorker: () => Worker
18
+ }
19
+ }
20
+ }
21
+
22
+ if (typeof window !== 'undefined' && !window.MonacoEnvironment) {
23
+ window.MonacoEnvironment = {
24
+ getWorker: () => new EditorWorker(),
25
+ }
26
+ }
27
+
28
+ const props = defineProps<{
29
+ modelValue: string
30
+ }>()
31
+
32
+ const emit = defineEmits<{
33
+ 'update:modelValue': [value: string]
34
+ save: []
35
+ }>()
36
+
37
+ const editorEl = ref<HTMLElement | null>(null)
38
+ let monaco: typeof Monaco | null = null
39
+ let editor: Monaco.editor.IStandaloneCodeEditor | null = null
40
+ let resizeObserver: ResizeObserver | null = null
41
+ let applyingExternalValue = false
42
+
43
+ watch(
44
+ () => props.modelValue,
45
+ (value) => {
46
+ if (!editor || value === editor.getValue()) {
47
+ return
48
+ }
49
+
50
+ applyingExternalValue = true
51
+ editor.setValue(value)
52
+ applyingExternalValue = false
53
+ },
54
+ )
55
+
56
+ onMounted(async () => {
57
+ const [loadedMonaco] = await Promise.all([
58
+ import('monaco-editor'),
59
+ import('monaco-editor/esm/vs/basic-languages/yaml/yaml.contribution'),
60
+ ])
61
+
62
+ monaco = loadedMonaco
63
+ await nextTick()
64
+
65
+ if (!editorEl.value) {
66
+ return
67
+ }
68
+
69
+ editor = monaco.editor.create(editorEl.value, {
70
+ value: props.modelValue,
71
+ language: 'yaml',
72
+ automaticLayout: true,
73
+ minimap: { enabled: false },
74
+ fontSize: 13,
75
+ lineNumbersMinChars: 3,
76
+ padding: { top: 12, bottom: 12 },
77
+ scrollBeyondLastLine: false,
78
+ tabSize: 2,
79
+ insertSpaces: true,
80
+ wordWrap: 'on',
81
+ wrappingIndent: 'same',
82
+ theme: document.documentElement.classList.contains('dark') ? 'vs-dark' : 'vs',
83
+ })
84
+
85
+ editor.onDidChangeModelContent(() => {
86
+ if (!editor || applyingExternalValue) {
87
+ return
88
+ }
89
+
90
+ emit('update:modelValue', editor.getValue())
91
+ })
92
+
93
+ editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter, () => {
94
+ emit('save')
95
+ })
96
+
97
+ resizeObserver = new ResizeObserver(() => {
98
+ editor?.layout()
99
+ })
100
+ resizeObserver.observe(editorEl.value)
101
+ })
102
+
103
+ onBeforeUnmount(() => {
104
+ resizeObserver?.disconnect()
105
+ resizeObserver = null
106
+ editor?.dispose()
107
+ editor = null
108
+ })
109
+ </script>
@@ -5,6 +5,7 @@
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "dependencies": {
8
+ "monaco-editor": "^0.55.1",
8
9
  "yaml": "^2.9.0"
9
10
  }
10
11
  }
@@ -8,12 +8,29 @@ importers:
8
8
 
9
9
  .:
10
10
  dependencies:
11
+ monaco-editor:
12
+ specifier: ^0.55.1
13
+ version: 0.55.1
11
14
  yaml:
12
15
  specifier: ^2.9.0
13
16
  version: 2.9.0
14
17
 
15
18
  packages:
16
19
 
20
+ '@types/trusted-types@2.0.7':
21
+ resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
22
+
23
+ dompurify@3.2.7:
24
+ resolution: {integrity: sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw==}
25
+
26
+ marked@14.0.0:
27
+ resolution: {integrity: sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==}
28
+ engines: {node: '>= 18'}
29
+ hasBin: true
30
+
31
+ monaco-editor@0.55.1:
32
+ resolution: {integrity: sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A==}
33
+
17
34
  yaml@2.9.0:
18
35
  resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==}
19
36
  engines: {node: '>= 14.6'}
@@ -21,4 +38,18 @@ packages:
21
38
 
22
39
  snapshots:
23
40
 
41
+ '@types/trusted-types@2.0.7':
42
+ optional: true
43
+
44
+ dompurify@3.2.7:
45
+ optionalDependencies:
46
+ '@types/trusted-types': 2.0.7
47
+
48
+ marked@14.0.0: {}
49
+
50
+ monaco-editor@0.55.1:
51
+ dependencies:
52
+ dompurify: 3.2.7
53
+ marked: 14.0.0
54
+
24
55
  yaml@2.9.0: {}
@@ -54,7 +54,7 @@
54
54
  class="fixed inset-0 z-50 flex items-center justify-center bg-black/35 p-4"
55
55
  @click.self="closeGroupConfigEditor"
56
56
  >
57
- <section class="w-full max-w-2xl rounded-lg border border-lightListBorder bg-lightDropdownOptionsBackground p-4 shadow-xl dark:border-darkListBorder dark:bg-darkDropdownOptionsBackground">
57
+ <section class="w-full max-w-5xl rounded-lg border border-lightListBorder bg-lightDropdownOptionsBackground p-4 shadow-xl dark:border-darkListBorder dark:bg-darkDropdownOptionsBackground">
58
58
  <header class="mb-3 flex items-center justify-between gap-3">
59
59
  <h2 class="m-0 text-base font-bold text-lightNavbarText dark:text-darkNavbarText">
60
60
  Group JSON
@@ -81,12 +81,9 @@
81
81
  </button>
82
82
  </header>
83
83
 
84
- <textarea
84
+ <YamlConfigEditor
85
85
  v-model="groupConfigCode"
86
- class="min-h-[500px] w-full resize-y rounded-lg border border-lightListBorder bg-lightListTable p-3 font-mono text-sm text-lightNavbarText outline-none focus:border-lightPrimaryButtonBackground dark:border-darkListBorder dark:bg-darkListTable dark:text-darkNavbarText dark:focus:border-darkPrimaryButtonBackground"
87
- spellcheck="false"
88
- @keydown.ctrl.enter.prevent="saveGroupConfig"
89
- @keydown.meta.enter.prevent="saveGroupConfig"
86
+ @save="saveGroupConfig"
90
87
  />
91
88
 
92
89
  <div
@@ -120,7 +117,7 @@
120
117
  class="fixed inset-0 z-50 flex items-center justify-center bg-black/35 p-4"
121
118
  @click.self="closeWidgetConfigEditor"
122
119
  >
123
- <section class="w-full max-w-2xl rounded-lg border border-lightListBorder bg-lightDropdownOptionsBackground p-4 shadow-xl dark:border-darkListBorder dark:bg-darkDropdownOptionsBackground">
120
+ <section class="w-full max-w-5xl rounded-lg border border-lightListBorder bg-lightDropdownOptionsBackground p-4 shadow-xl dark:border-darkListBorder dark:bg-darkDropdownOptionsBackground">
124
121
  <header class="mb-3 flex items-center justify-between gap-3">
125
122
  <h2 class="m-0 text-base font-bold text-lightNavbarText dark:text-darkNavbarText">
126
123
  Widget JSON
@@ -147,12 +144,9 @@
147
144
  </button>
148
145
  </header>
149
146
 
150
- <textarea
147
+ <YamlConfigEditor
151
148
  v-model="widgetConfigCode"
152
- class="min-h-[500px] w-full resize-y rounded-lg border border-lightListBorder bg-lightListTable p-3 font-mono text-sm text-lightNavbarText outline-none focus:border-lightPrimaryButtonBackground dark:border-darkListBorder dark:bg-darkListTable dark:text-darkNavbarText dark:focus:border-darkPrimaryButtonBackground"
153
- spellcheck="false"
154
- @keydown.ctrl.enter.prevent="saveWidgetConfig"
155
- @keydown.meta.enter.prevent="saveWidgetConfig"
149
+ @save="saveWidgetConfig"
156
150
  />
157
151
 
158
152
  <div
@@ -203,6 +197,7 @@ import { computed, ref, watch } from 'vue'
203
197
  import { parse as parseYaml, stringify as stringifyYaml } from 'yaml'
204
198
  import { Button } from '@/afcl'
205
199
  import DashboardGroup from './DashboardGroup.vue'
200
+ import YamlConfigEditor from './YamlConfigEditor.vue'
206
201
  import { DashboardApiError, dashboardApi, type DashboardResponse } from '../api/dashboardApi.js'
207
202
  import type {
208
203
  DashboardConfig,
@@ -0,0 +1,109 @@
1
+ <template>
2
+ <div
3
+ ref="editorEl"
4
+ class="min-h-[500px] w-full overflow-hidden rounded-lg border border-lightListBorder bg-lightListTable text-sm dark:border-darkListBorder dark:bg-darkListTable"
5
+ />
6
+ </template>
7
+
8
+ <script setup lang="ts">
9
+ import 'monaco-editor/min/vs/editor/editor.main.css'
10
+ import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker'
11
+ import { nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
12
+ import type * as Monaco from 'monaco-editor'
13
+
14
+ declare global {
15
+ interface Window {
16
+ MonacoEnvironment?: {
17
+ getWorker: () => Worker
18
+ }
19
+ }
20
+ }
21
+
22
+ if (typeof window !== 'undefined' && !window.MonacoEnvironment) {
23
+ window.MonacoEnvironment = {
24
+ getWorker: () => new EditorWorker(),
25
+ }
26
+ }
27
+
28
+ const props = defineProps<{
29
+ modelValue: string
30
+ }>()
31
+
32
+ const emit = defineEmits<{
33
+ 'update:modelValue': [value: string]
34
+ save: []
35
+ }>()
36
+
37
+ const editorEl = ref<HTMLElement | null>(null)
38
+ let monaco: typeof Monaco | null = null
39
+ let editor: Monaco.editor.IStandaloneCodeEditor | null = null
40
+ let resizeObserver: ResizeObserver | null = null
41
+ let applyingExternalValue = false
42
+
43
+ watch(
44
+ () => props.modelValue,
45
+ (value) => {
46
+ if (!editor || value === editor.getValue()) {
47
+ return
48
+ }
49
+
50
+ applyingExternalValue = true
51
+ editor.setValue(value)
52
+ applyingExternalValue = false
53
+ },
54
+ )
55
+
56
+ onMounted(async () => {
57
+ const [loadedMonaco] = await Promise.all([
58
+ import('monaco-editor'),
59
+ import('monaco-editor/esm/vs/basic-languages/yaml/yaml.contribution'),
60
+ ])
61
+
62
+ monaco = loadedMonaco
63
+ await nextTick()
64
+
65
+ if (!editorEl.value) {
66
+ return
67
+ }
68
+
69
+ editor = monaco.editor.create(editorEl.value, {
70
+ value: props.modelValue,
71
+ language: 'yaml',
72
+ automaticLayout: true,
73
+ minimap: { enabled: false },
74
+ fontSize: 13,
75
+ lineNumbersMinChars: 3,
76
+ padding: { top: 12, bottom: 12 },
77
+ scrollBeyondLastLine: false,
78
+ tabSize: 2,
79
+ insertSpaces: true,
80
+ wordWrap: 'on',
81
+ wrappingIndent: 'same',
82
+ theme: document.documentElement.classList.contains('dark') ? 'vs-dark' : 'vs',
83
+ })
84
+
85
+ editor.onDidChangeModelContent(() => {
86
+ if (!editor || applyingExternalValue) {
87
+ return
88
+ }
89
+
90
+ emit('update:modelValue', editor.getValue())
91
+ })
92
+
93
+ editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter, () => {
94
+ emit('save')
95
+ })
96
+
97
+ resizeObserver = new ResizeObserver(() => {
98
+ editor?.layout()
99
+ })
100
+ resizeObserver.observe(editorEl.value)
101
+ })
102
+
103
+ onBeforeUnmount(() => {
104
+ resizeObserver?.disconnect()
105
+ resizeObserver = null
106
+ editor?.dispose()
107
+ editor = null
108
+ })
109
+ </script>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adminforth/dashboard",
3
- "version": "1.6.0",
3
+ "version": "1.7.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
package/shims-vue.d.ts CHANGED
@@ -3,3 +3,14 @@ declare module '*.vue' {
3
3
  const component: DefineComponent<{}, {}, any>;
4
4
  export default component;
5
5
  }
6
+
7
+ declare module '*.css';
8
+
9
+ declare module '*?worker' {
10
+ const WorkerFactory: {
11
+ new (): Worker;
12
+ };
13
+ export default WorkerFactory;
14
+ }
15
+
16
+ declare module 'monaco-editor/esm/vs/basic-languages/yaml/yaml.contribution';
package/tsconfig.json CHANGED
@@ -11,7 +11,9 @@
11
11
  "skipLibCheck": true,
12
12
  "paths": {
13
13
  "@/afcl": ["./node_modules/adminforth/dist/spa/src/afcl/index.ts"],
14
- "@/*": ["./node_modules/adminforth/dist/spa/src/*"]
14
+ "@/*": ["./node_modules/adminforth/dist/spa/src/*"],
15
+ "monaco-editor": ["./custom/node_modules/monaco-editor/esm/vs/editor/editor.api.d.ts"],
16
+ "monaco-editor/*": ["./custom/node_modules/monaco-editor/*"]
15
17
  }
16
18
  },
17
19
  "exclude": ["node_modules", "dist"]