@aerogel/core 0.0.0-next.b85327579d32f21c6a9fa21142f0165cdd320d7e → 0.0.0-next.d824b40e5d06757cd9f47c9f771d916185df4f05
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/dist/aerogel-core.cjs.js +1 -1
- package/dist/aerogel-core.cjs.js.map +1 -1
- package/dist/aerogel-core.d.ts +355 -59
- package/dist/aerogel-core.esm.js +1 -1
- package/dist/aerogel-core.esm.js.map +1 -1
- package/dist/virtual.d.ts +11 -0
- package/noeldemartin.config.js +4 -1
- package/package.json +3 -3
- package/src/bootstrap/index.ts +2 -1
- package/src/components/AGAppModals.vue +15 -0
- package/src/components/AGAppOverlays.vue +5 -7
- package/src/components/AGAppSnackbars.vue +13 -0
- package/src/components/basic/AGErrorMessage.vue +16 -0
- package/src/components/basic/AGLink.vue +9 -0
- package/src/components/basic/AGMarkdown.vue +7 -6
- package/src/components/basic/index.ts +3 -1
- package/src/components/constants.ts +8 -0
- package/src/components/forms/AGButton.vue +25 -15
- package/src/components/headless/forms/AGHeadlessButton.vue +4 -3
- package/src/components/headless/index.ts +1 -0
- package/src/components/headless/snackbars/AGHeadlessSnackbar.vue +10 -0
- package/src/components/headless/snackbars/index.ts +25 -0
- package/src/components/index.ts +2 -0
- package/src/components/modals/AGAlertModal.vue +0 -1
- package/src/components/modals/AGConfirmModal.vue +1 -1
- package/src/components/modals/AGErrorReportModal.ts +20 -0
- package/src/components/modals/AGErrorReportModal.vue +62 -0
- package/src/components/modals/AGErrorReportModalButtons.vue +109 -0
- package/src/components/modals/AGErrorReportModalTitle.vue +25 -0
- package/src/components/modals/AGModal.vue +3 -2
- package/src/components/modals/AGModalTitle.vue +9 -0
- package/src/components/modals/index.ts +17 -2
- package/src/components/snackbars/AGSnackbar.vue +42 -0
- package/src/components/snackbars/index.ts +3 -0
- package/src/directives/index.ts +16 -3
- package/src/errors/Errors.ts +60 -9
- package/src/errors/index.ts +39 -1
- package/src/forms/Form.ts +3 -3
- package/src/lang/Lang.ts +1 -1
- package/src/main.ts +0 -2
- package/src/plugins/Plugin.ts +1 -0
- package/src/plugins/index.ts +19 -0
- package/src/services/App.state.ts +8 -3
- package/src/services/App.ts +5 -2
- package/src/services/Service.ts +7 -2
- package/src/services/index.ts +6 -3
- package/src/types/virtual.d.ts +11 -0
- package/src/ui/UI.state.ts +10 -1
- package/src/ui/UI.ts +37 -8
- package/src/ui/index.ts +4 -0
- package/src/utils/markdown.ts +11 -2
- package/src/utils/vue.ts +2 -0
- package/tsconfig.json +1 -0
- package/vite.config.ts +2 -1
- package/src/globals.ts +0 -6
package/src/errors/Errors.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { JSError, facade, isObject } from '@noeldemartin/utils';
|
|
1
|
+
import { JSError, facade, isObject, objectWithoutEmpty, toString } from '@noeldemartin/utils';
|
|
2
2
|
|
|
3
3
|
import App from '@/services/App';
|
|
4
4
|
import ServiceBootError from '@/errors/ServiceBootError';
|
|
5
|
-
import UI from '@/ui/UI';
|
|
6
|
-
import {
|
|
5
|
+
import UI, { UIComponents } from '@/ui/UI';
|
|
6
|
+
import { translateWithDefault } from '@/lang/utils';
|
|
7
7
|
|
|
8
8
|
import Service from './Errors.state';
|
|
9
|
+
import { Colors } from '@/components/constants';
|
|
9
10
|
import type { ErrorReport, ErrorReportLog, ErrorSource } from './Errors.state';
|
|
10
11
|
|
|
11
12
|
export class ErrorsService extends Service {
|
|
@@ -24,12 +25,17 @@ export class ErrorsService extends Service {
|
|
|
24
25
|
public async inspect(error: ErrorSource | ErrorReport[]): Promise<void> {
|
|
25
26
|
const reports = Array.isArray(error) ? error : [await this.createErrorReport(error)];
|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
if (reports.length === 0) {
|
|
29
|
+
UI.alert(translateWithDefault('errors.inspectEmpty', 'Nothing to inspect!'));
|
|
30
|
+
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
UI.openModal(UI.requireComponent(UIComponents.ErrorReportModal), { reports });
|
|
29
35
|
}
|
|
30
36
|
|
|
31
37
|
public async report(error: ErrorSource, message?: string): Promise<void> {
|
|
32
|
-
if (App.
|
|
38
|
+
if (App.development || App.testing) {
|
|
33
39
|
this.logError(error);
|
|
34
40
|
}
|
|
35
41
|
|
|
@@ -54,8 +60,23 @@ export class ErrorsService extends Service {
|
|
|
54
60
|
date: new Date(),
|
|
55
61
|
};
|
|
56
62
|
|
|
57
|
-
|
|
58
|
-
|
|
63
|
+
UI.showSnackbar(
|
|
64
|
+
message ??
|
|
65
|
+
translateWithDefault('errors.notice', 'Something went wrong, but it\'s not your fault. Try again!'),
|
|
66
|
+
{
|
|
67
|
+
color: Colors.Danger,
|
|
68
|
+
actions: [
|
|
69
|
+
{
|
|
70
|
+
text: translateWithDefault('errors.viewDetails', 'View details'),
|
|
71
|
+
dismiss: true,
|
|
72
|
+
handler: () =>
|
|
73
|
+
UI.openModal(UI.requireComponent(UIComponents.ErrorReportModal), {
|
|
74
|
+
reports: [report],
|
|
75
|
+
}),
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
},
|
|
79
|
+
);
|
|
59
80
|
|
|
60
81
|
this.setState({ logs: [log].concat(this.logs) });
|
|
61
82
|
}
|
|
@@ -84,6 +105,22 @@ export class ErrorsService extends Service {
|
|
|
84
105
|
});
|
|
85
106
|
}
|
|
86
107
|
|
|
108
|
+
public getErrorMessage(error: ErrorSource): string {
|
|
109
|
+
if (typeof error === 'string') {
|
|
110
|
+
return error;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (error instanceof Error || error instanceof JSError) {
|
|
114
|
+
return error.message;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (isObject(error)) {
|
|
118
|
+
return toString(error['message'] ?? error['description'] ?? 'Unknown error object');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return translateWithDefault('errors.unknown', 'Unknown Error');
|
|
122
|
+
}
|
|
123
|
+
|
|
87
124
|
private logError(error: unknown): void {
|
|
88
125
|
// eslint-disable-next-line no-console
|
|
89
126
|
console.error(error);
|
|
@@ -102,8 +139,22 @@ export class ErrorsService extends Service {
|
|
|
102
139
|
return this.createErrorReportFromError(error);
|
|
103
140
|
}
|
|
104
141
|
|
|
142
|
+
if (isObject(error)) {
|
|
143
|
+
return objectWithoutEmpty({
|
|
144
|
+
title: toString(
|
|
145
|
+
error['name'] ?? error['title'] ?? translateWithDefault('errors.unknown', 'Unknown Error'),
|
|
146
|
+
),
|
|
147
|
+
description: toString(
|
|
148
|
+
error['message'] ??
|
|
149
|
+
error['description'] ??
|
|
150
|
+
translateWithDefault('errors.unknownDescription', 'Unknown error object'),
|
|
151
|
+
),
|
|
152
|
+
error,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
105
156
|
return {
|
|
106
|
-
title:
|
|
157
|
+
title: translateWithDefault('errors.unknown', 'Unknown Error'),
|
|
107
158
|
error,
|
|
108
159
|
};
|
|
109
160
|
}
|
package/src/errors/index.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { tap } from '@noeldemartin/utils';
|
|
2
|
+
|
|
1
3
|
import { bootServices } from '@/services';
|
|
2
4
|
import { definePlugin } from '@/plugins';
|
|
3
5
|
|
|
@@ -7,15 +9,51 @@ import { ErrorReport, ErrorReportLog, ErrorSource } from './Errors.state';
|
|
|
7
9
|
export { Errors, ErrorSource, ErrorReport, ErrorReportLog };
|
|
8
10
|
|
|
9
11
|
const services = { $errors: Errors };
|
|
12
|
+
const frameworkHandler: ErrorHandler = (error) => {
|
|
13
|
+
if (!Errors.instance) {
|
|
14
|
+
// eslint-disable-next-line no-console
|
|
15
|
+
console.warn('Errors service hasn\'t been initialized properly!');
|
|
16
|
+
|
|
17
|
+
// eslint-disable-next-line no-console
|
|
18
|
+
console.error(error);
|
|
19
|
+
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
10
22
|
|
|
23
|
+
Errors.report(error);
|
|
24
|
+
|
|
25
|
+
return true;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
function setUpErrorHandler(baseHandler: ErrorHandler = () => false): ErrorHandler {
|
|
29
|
+
return tap(
|
|
30
|
+
(error) => baseHandler(error) || frameworkHandler(error),
|
|
31
|
+
(errorHandler) => {
|
|
32
|
+
globalThis.onerror = (message, _, __, ___, error) => errorHandler(error ?? message);
|
|
33
|
+
globalThis.onunhandledrejection = (event) => errorHandler(event.reason);
|
|
34
|
+
},
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type ErrorHandler = (error: ErrorSource) => boolean;
|
|
11
39
|
export type ErrorsServices = typeof services;
|
|
12
40
|
|
|
13
41
|
export default definePlugin({
|
|
14
|
-
async install(app) {
|
|
42
|
+
async install(app, options) {
|
|
43
|
+
const errorHandler = setUpErrorHandler(options.handleError);
|
|
44
|
+
|
|
45
|
+
app.config.errorHandler = errorHandler;
|
|
46
|
+
|
|
15
47
|
await bootServices(app, services);
|
|
16
48
|
},
|
|
17
49
|
});
|
|
18
50
|
|
|
51
|
+
declare module '@/bootstrap/options' {
|
|
52
|
+
interface AerogelOptions {
|
|
53
|
+
handleError?(error: ErrorSource): boolean;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
19
57
|
declare module '@/services' {
|
|
20
58
|
export interface Services extends ErrorsServices {}
|
|
21
59
|
}
|
package/src/forms/Form.ts
CHANGED
|
@@ -92,11 +92,11 @@ export default class Form<Fields extends FormFieldDefinitions = FormFieldDefinit
|
|
|
92
92
|
return this.valid;
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
public reset(): void {
|
|
95
|
+
public reset(options: { keepData?: boolean; keepErrors?: boolean } = {}): void {
|
|
96
96
|
this._submitted.value = false;
|
|
97
97
|
|
|
98
|
-
this.resetData();
|
|
99
|
-
this.resetErrors();
|
|
98
|
+
options.keepData || this.resetData();
|
|
99
|
+
options.keepErrors || this.resetErrors();
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
public submit(): boolean {
|
package/src/lang/Lang.ts
CHANGED
|
@@ -17,7 +17,7 @@ export class LangService extends Service {
|
|
|
17
17
|
this.provider = {
|
|
18
18
|
translate: (key) => {
|
|
19
19
|
// eslint-disable-next-line no-console
|
|
20
|
-
App.
|
|
20
|
+
App.development && console.warn('Lang provider is missing');
|
|
21
21
|
|
|
22
22
|
return key;
|
|
23
23
|
},
|
package/src/main.ts
CHANGED
package/src/plugins/Plugin.ts
CHANGED
package/src/plugins/index.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import type { GetClosureArgs } from '@noeldemartin/utils';
|
|
2
|
+
|
|
3
|
+
import App from '@/services/App';
|
|
4
|
+
|
|
1
5
|
import type { Plugin } from './Plugin';
|
|
2
6
|
|
|
3
7
|
export * from './Plugin';
|
|
@@ -5,3 +9,18 @@ export * from './Plugin';
|
|
|
5
9
|
export function definePlugin<T extends Plugin>(plugin: T): T {
|
|
6
10
|
return plugin;
|
|
7
11
|
}
|
|
12
|
+
|
|
13
|
+
export async function installPlugins(plugins: Plugin[], ...args: GetClosureArgs<Plugin['install']>): Promise<void> {
|
|
14
|
+
App.setState(
|
|
15
|
+
'plugins',
|
|
16
|
+
plugins.reduce((pluginsMap, plugin) => {
|
|
17
|
+
if (plugin.name) {
|
|
18
|
+
pluginsMap[plugin.name] = plugin;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return pluginsMap;
|
|
22
|
+
}, {} as Record<string, Plugin>),
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
await Promise.all(plugins.map((plugin) => plugin.install(...args)) ?? []);
|
|
26
|
+
}
|
|
@@ -1,13 +1,18 @@
|
|
|
1
|
+
import Build from 'virtual:aerogel';
|
|
2
|
+
|
|
1
3
|
import { defineServiceState } from '@/services/Service';
|
|
4
|
+
import type { Plugin } from '@/plugins/Plugin';
|
|
2
5
|
|
|
3
6
|
export default defineServiceState({
|
|
4
7
|
name: 'app',
|
|
5
8
|
initialState: {
|
|
6
|
-
|
|
9
|
+
plugins: {} as Record<string, Plugin>,
|
|
10
|
+
environment: Build.environment,
|
|
11
|
+
sourceUrl: Build.sourceUrl,
|
|
7
12
|
isMounted: false,
|
|
8
13
|
},
|
|
9
14
|
computed: {
|
|
10
|
-
|
|
11
|
-
|
|
15
|
+
development: (state) => state.environment === 'development',
|
|
16
|
+
testing: (state) => state.environment === 'testing',
|
|
12
17
|
},
|
|
13
18
|
});
|
package/src/services/App.ts
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import { facade } from '@noeldemartin/utils';
|
|
2
2
|
|
|
3
3
|
import Events from '@/services/Events';
|
|
4
|
+
import type { Plugin } from '@/plugins';
|
|
4
5
|
|
|
5
6
|
import Service from './App.state';
|
|
6
7
|
|
|
7
8
|
export class AppService extends Service {
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
public plugin<T extends Plugin = Plugin>(name: string): T | null {
|
|
11
|
+
return (this.plugins[name] as T) ?? null;
|
|
12
|
+
}
|
|
11
13
|
|
|
14
|
+
protected async boot(): Promise<void> {
|
|
12
15
|
Events.once('application-mounted', () => this.setState({ isMounted: true }));
|
|
13
16
|
}
|
|
14
17
|
|
package/src/services/Service.ts
CHANGED
|
@@ -93,7 +93,8 @@ export default class Service<
|
|
|
93
93
|
const handleError = (error: unknown) => this._booted.reject(new ServiceBootError(this._name, error));
|
|
94
94
|
|
|
95
95
|
try {
|
|
96
|
-
this.
|
|
96
|
+
this.frameworkBoot()
|
|
97
|
+
.then(() => this.boot())
|
|
97
98
|
.then(() => this._booted.resolve())
|
|
98
99
|
.catch(handleError);
|
|
99
100
|
} catch (error) {
|
|
@@ -189,10 +190,14 @@ export default class Service<
|
|
|
189
190
|
return state;
|
|
190
191
|
}
|
|
191
192
|
|
|
192
|
-
protected async
|
|
193
|
+
protected async frameworkBoot(): Promise<void> {
|
|
193
194
|
this.restorePersistedState();
|
|
194
195
|
}
|
|
195
196
|
|
|
197
|
+
protected async boot(): Promise<void> {
|
|
198
|
+
// Override.
|
|
199
|
+
}
|
|
200
|
+
|
|
196
201
|
protected restorePersistedState(): void {
|
|
197
202
|
// TODO fix this.static()
|
|
198
203
|
const persist = (this.constructor as unknown as { persist: string[] }).persist;
|
package/src/services/index.ts
CHANGED
|
@@ -24,13 +24,16 @@ export interface Services extends DefaultServices {}
|
|
|
24
24
|
|
|
25
25
|
export async function bootServices(app: VueApp, services: Record<string, Service>): Promise<void> {
|
|
26
26
|
await Promise.all(
|
|
27
|
-
Object.entries(services).map(async ([
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
Object.entries(services).map(async ([name, service]) => {
|
|
28
|
+
await service
|
|
29
|
+
.launch()
|
|
30
|
+
.catch((error) => app.config.errorHandler?.(error, null, `Failed launching ${name}.`));
|
|
30
31
|
}),
|
|
31
32
|
);
|
|
32
33
|
|
|
33
34
|
Object.assign(app.config.globalProperties, services);
|
|
35
|
+
|
|
36
|
+
App.development && Object.assign(window, services);
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
export default definePlugin({
|
package/src/ui/UI.state.ts
CHANGED
|
@@ -17,7 +17,16 @@ export interface ModalComponent<
|
|
|
17
17
|
Result = unknown
|
|
18
18
|
> {}
|
|
19
19
|
|
|
20
|
+
export interface Snackbar {
|
|
21
|
+
id: string;
|
|
22
|
+
component: Component;
|
|
23
|
+
properties: Record<string, unknown>;
|
|
24
|
+
}
|
|
25
|
+
|
|
20
26
|
export default defineServiceState({
|
|
21
27
|
name: 'ui',
|
|
22
|
-
initialState: {
|
|
28
|
+
initialState: {
|
|
29
|
+
modals: [] as Modal[],
|
|
30
|
+
snackbars: [] as Snackbar[],
|
|
31
|
+
},
|
|
23
32
|
});
|
package/src/ui/UI.ts
CHANGED
|
@@ -4,9 +4,10 @@ import type { Component } from 'vue';
|
|
|
4
4
|
import type { ObjectValues } from '@noeldemartin/utils';
|
|
5
5
|
|
|
6
6
|
import Events from '@/services/Events';
|
|
7
|
+
import type { SnackbarAction, SnackbarColor } from '@/components/headless/snackbars';
|
|
7
8
|
|
|
8
9
|
import Service from './UI.state';
|
|
9
|
-
import type { Modal, ModalComponent } from './UI.state';
|
|
10
|
+
import type { Modal, ModalComponent, Snackbar } from './UI.state';
|
|
10
11
|
|
|
11
12
|
interface ModalCallbacks<T = unknown> {
|
|
12
13
|
willClose(result: T | undefined): void;
|
|
@@ -21,16 +22,28 @@ type ModalResult<TComponent> = TComponent extends ModalComponent<Record<string,
|
|
|
21
22
|
export const UIComponents = {
|
|
22
23
|
AlertModal: 'alert-modal',
|
|
23
24
|
ConfirmModal: 'confirm-modal',
|
|
25
|
+
ErrorReportModal: 'error-report-modal',
|
|
24
26
|
LoadingModal: 'loading-modal',
|
|
27
|
+
Snackbar: 'snackbar',
|
|
25
28
|
} as const;
|
|
26
29
|
|
|
27
30
|
export type UIComponent = ObjectValues<typeof UIComponents>;
|
|
28
31
|
|
|
32
|
+
export interface ShowSnackbarOptions {
|
|
33
|
+
component?: Component;
|
|
34
|
+
color?: SnackbarColor;
|
|
35
|
+
actions?: SnackbarAction[];
|
|
36
|
+
}
|
|
37
|
+
|
|
29
38
|
export class UIService extends Service {
|
|
30
39
|
|
|
31
40
|
private modalCallbacks: Record<string, Partial<ModalCallbacks>> = {};
|
|
32
41
|
private components: Partial<Record<UIComponent, Component>> = {};
|
|
33
42
|
|
|
43
|
+
public requireComponent(name: UIComponent): Component {
|
|
44
|
+
return this.components[name] ?? fail(`UI Component '${name}' is not defined!`);
|
|
45
|
+
}
|
|
46
|
+
|
|
34
47
|
public alert(message: string): void;
|
|
35
48
|
public alert(title: string, message: string): void;
|
|
36
49
|
public alert(messageOrTitle: string, message?: string): void {
|
|
@@ -66,6 +79,25 @@ export class UIService extends Service {
|
|
|
66
79
|
return result;
|
|
67
80
|
}
|
|
68
81
|
|
|
82
|
+
public showSnackbar(message: string, options: ShowSnackbarOptions = {}): void {
|
|
83
|
+
const snackbar: Snackbar = {
|
|
84
|
+
id: uuid(),
|
|
85
|
+
properties: { message, ...options },
|
|
86
|
+
component: options.component ?? markRaw(this.requireComponent(UIComponents.Snackbar)),
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
this.setState('snackbars', this.snackbars.concat(snackbar));
|
|
90
|
+
|
|
91
|
+
setTimeout(() => this.hideSnackbar(snackbar.id), 5000);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
public hideSnackbar(id: string): void {
|
|
95
|
+
this.setState(
|
|
96
|
+
'snackbars',
|
|
97
|
+
this.snackbars.filter((snackbar) => snackbar.id !== id),
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
69
101
|
public registerComponent(name: UIComponent, component: Component): void {
|
|
70
102
|
this.components[name] = component;
|
|
71
103
|
}
|
|
@@ -105,15 +137,9 @@ export class UIService extends Service {
|
|
|
105
137
|
}
|
|
106
138
|
|
|
107
139
|
protected async boot(): Promise<void> {
|
|
108
|
-
await super.boot();
|
|
109
|
-
|
|
110
140
|
this.watchModalEvents();
|
|
111
141
|
}
|
|
112
142
|
|
|
113
|
-
private requireComponent(name: UIComponent): Component {
|
|
114
|
-
return this.components[name] ?? fail(`UI Component '${name}' is not defined!`);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
143
|
private watchModalEvents(): void {
|
|
118
144
|
Events.on('modal-will-close', ({ modal, result }) => {
|
|
119
145
|
this.modalCallbacks[modal.id]?.willClose?.(result);
|
|
@@ -124,7 +150,10 @@ export class UIService extends Service {
|
|
|
124
150
|
});
|
|
125
151
|
|
|
126
152
|
Events.on('modal-closed', async ({ modal, result }) => {
|
|
127
|
-
this.setState(
|
|
153
|
+
this.setState(
|
|
154
|
+
'modals',
|
|
155
|
+
this.modals.filter((m) => m.id !== modal.id),
|
|
156
|
+
);
|
|
128
157
|
|
|
129
158
|
this.modalCallbacks[modal.id]?.closed?.(result);
|
|
130
159
|
|
package/src/ui/index.ts
CHANGED
|
@@ -6,7 +6,9 @@ import { definePlugin } from '@/plugins';
|
|
|
6
6
|
import UI, { UIComponents } from './UI';
|
|
7
7
|
import AGAlertModal from '../components/modals/AGAlertModal.vue';
|
|
8
8
|
import AGConfirmModal from '../components/modals/AGConfirmModal.vue';
|
|
9
|
+
import AGErrorReportModal from '../components/modals/AGErrorReportModal.vue';
|
|
9
10
|
import AGLoadingModal from '../components/modals/AGLoadingModal.vue';
|
|
11
|
+
import AGSnackbar from '../components/snackbars/AGSnackbar.vue';
|
|
10
12
|
import type { UIComponent } from './UI';
|
|
11
13
|
|
|
12
14
|
export { UI, UIComponents, UIComponent };
|
|
@@ -20,7 +22,9 @@ export default definePlugin({
|
|
|
20
22
|
const defaultComponents = {
|
|
21
23
|
[UIComponents.AlertModal]: AGAlertModal,
|
|
22
24
|
[UIComponents.ConfirmModal]: AGConfirmModal,
|
|
25
|
+
[UIComponents.ErrorReportModal]: AGErrorReportModal,
|
|
23
26
|
[UIComponents.LoadingModal]: AGLoadingModal,
|
|
27
|
+
[UIComponents.Snackbar]: AGSnackbar,
|
|
24
28
|
};
|
|
25
29
|
|
|
26
30
|
Object.entries({
|
package/src/utils/markdown.ts
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
|
+
import { tap } from '@noeldemartin/utils';
|
|
1
2
|
import DOMPurify from 'dompurify';
|
|
2
|
-
import { marked } from 'marked';
|
|
3
|
+
import { Renderer, marked } from 'marked';
|
|
4
|
+
|
|
5
|
+
function makeRenderer(): Renderer {
|
|
6
|
+
return tap(new Renderer(), (renderer) => {
|
|
7
|
+
renderer.link = function(href, title, text) {
|
|
8
|
+
return Renderer.prototype.link.apply(this, [href, title, text]).replace('<a', '<a target="_blank"');
|
|
9
|
+
};
|
|
10
|
+
});
|
|
11
|
+
}
|
|
3
12
|
|
|
4
13
|
export function renderMarkdown(markdown: string): string {
|
|
5
|
-
return safeHtml(marked(markdown, { mangle: false, headerIds: false }));
|
|
14
|
+
return safeHtml(marked(markdown, { mangle: false, headerIds: false, renderer: makeRenderer() }));
|
|
6
15
|
}
|
|
7
16
|
|
|
8
17
|
export function safeHtml(html: string): string {
|
package/src/utils/vue.ts
CHANGED
|
@@ -10,6 +10,8 @@ type BaseProp<T> = {
|
|
|
10
10
|
type RequiredProp<T> = BaseProp<T> & { required: true };
|
|
11
11
|
type OptionalProp<T> = BaseProp<T> & { default: T | (() => T) | null };
|
|
12
12
|
|
|
13
|
+
export type ComponentProps = Record<string, unknown>;
|
|
14
|
+
|
|
13
15
|
export function arrayProp<T>(defaultValue?: () => T[]): OptionalProp<T[]> {
|
|
14
16
|
return {
|
|
15
17
|
type: Array as PropType<T[]>,
|
package/tsconfig.json
CHANGED
package/vite.config.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import Aerogel from '@aerogel/vite';
|
|
2
|
+
import Icons from 'unplugin-icons/vite';
|
|
2
3
|
import { defineConfig } from 'vitest/config';
|
|
3
4
|
import { resolve } from 'path';
|
|
4
5
|
|
|
5
6
|
export default defineConfig({
|
|
6
7
|
test: { clearMocks: true },
|
|
7
|
-
plugins: [Aerogel()],
|
|
8
|
+
plugins: [Aerogel(), Icons()],
|
|
8
9
|
resolve: {
|
|
9
10
|
alias: {
|
|
10
11
|
'@': resolve(__dirname, './src'),
|