@aerogel/core 0.0.0-next.824cf5311c4335d119158f507dad094ed4f4f0b6 → 0.0.0-next.8e6b2bcc764fa682decbb41aa6848c77a744dec3
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 +41 -9
- package/dist/aerogel-core.esm.js +1 -1
- package/dist/aerogel-core.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/bootstrap/index.ts +2 -1
- package/src/components/headless/forms/AGHeadlessButton.vue +8 -8
- package/src/components/headless/forms/AGHeadlessInputInput.vue +2 -0
- package/src/components/lib/AGErrorMessage.vue +2 -2
- package/src/directives/index.ts +2 -0
- package/src/directives/measure.ts +11 -2
- package/src/errors/Errors.ts +9 -16
- package/src/errors/index.ts +1 -0
- package/src/errors/utils.ts +19 -0
- package/src/lang/Lang.ts +10 -22
- package/src/main.ts +2 -0
- package/src/services/Events.ts +12 -0
- package/src/testing/index.ts +25 -0
- package/src/ui/UI.ts +5 -3
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aerogel/core",
|
|
3
3
|
"description": "The Lightest Solid",
|
|
4
|
-
"version": "0.0.0-next.
|
|
4
|
+
"version": "0.0.0-next.8e6b2bcc764fa682decbb41aa6848c77a744dec3",
|
|
5
5
|
"main": "dist/aerogel-core.cjs.js",
|
|
6
6
|
"module": "dist/aerogel-core.esm.js",
|
|
7
7
|
"types": "dist/aerogel-core.d.ts",
|
package/src/bootstrap/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ import errors from '@/errors';
|
|
|
6
6
|
import Events from '@/services/Events';
|
|
7
7
|
import lang from '@/lang';
|
|
8
8
|
import services from '@/services';
|
|
9
|
+
import testing from '@/testing';
|
|
9
10
|
import ui from '@/ui';
|
|
10
11
|
import { installPlugins } from '@/plugins';
|
|
11
12
|
import type { AerogelOptions } from '@/bootstrap/options';
|
|
@@ -13,7 +14,7 @@ import type { AerogelOptions } from '@/bootstrap/options';
|
|
|
13
14
|
export { AerogelOptions };
|
|
14
15
|
|
|
15
16
|
export async function bootstrapApplication(app: App, options: AerogelOptions = {}): Promise<void> {
|
|
16
|
-
const plugins = [directives, errors, lang, services, ui, ...(options.plugins ?? [])];
|
|
17
|
+
const plugins = [testing, directives, errors, lang, services, ui, ...(options.plugins ?? [])];
|
|
17
18
|
|
|
18
19
|
await installPlugins(plugins, app, options);
|
|
19
20
|
await options.install?.(app);
|
|
@@ -10,7 +10,7 @@ import { objectWithoutEmpty } from '@noeldemartin/utils';
|
|
|
10
10
|
|
|
11
11
|
import { booleanProp, objectProp, stringProp } from '@/utils/vue';
|
|
12
12
|
|
|
13
|
-
const
|
|
13
|
+
const props = defineProps({
|
|
14
14
|
href: stringProp(),
|
|
15
15
|
url: stringProp(),
|
|
16
16
|
route: stringProp(),
|
|
@@ -20,32 +20,32 @@ const { href, url, route, routeParams, routeQuery, submit } = defineProps({
|
|
|
20
20
|
});
|
|
21
21
|
|
|
22
22
|
const component = computed(() => {
|
|
23
|
-
if (route) {
|
|
23
|
+
if (props.route) {
|
|
24
24
|
return {
|
|
25
25
|
tag: 'router-link',
|
|
26
26
|
props: {
|
|
27
27
|
to: objectWithoutEmpty({
|
|
28
|
-
name: route,
|
|
29
|
-
params: routeParams,
|
|
30
|
-
query: routeQuery,
|
|
28
|
+
name: props.route,
|
|
29
|
+
params: props.routeParams,
|
|
30
|
+
query: props.routeQuery,
|
|
31
31
|
}),
|
|
32
32
|
},
|
|
33
33
|
};
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
if (href || url) {
|
|
36
|
+
if (props.href || props.url) {
|
|
37
37
|
return {
|
|
38
38
|
tag: 'a',
|
|
39
39
|
props: {
|
|
40
40
|
target: '_blank',
|
|
41
|
-
href: href || url,
|
|
41
|
+
href: props.href || props.url,
|
|
42
42
|
},
|
|
43
43
|
};
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
return {
|
|
47
47
|
tag: 'button',
|
|
48
|
-
props: { type: submit ? 'submit' : 'button' },
|
|
48
|
+
props: { type: props.submit ? 'submit' : 'button' },
|
|
49
49
|
};
|
|
50
50
|
});
|
|
51
51
|
</script>
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
<input
|
|
3
3
|
:id="input.id"
|
|
4
4
|
ref="$input"
|
|
5
|
+
:name="name"
|
|
5
6
|
:type="type"
|
|
6
7
|
:value="value"
|
|
7
8
|
:aria-invalid="input.errors ? 'true' : 'false'"
|
|
@@ -26,6 +27,7 @@ const input = injectReactiveOrFail<IAGHeadlessInput>(
|
|
|
26
27
|
'input',
|
|
27
28
|
'<AGHeadlessInputInput> must be a child of a <AGHeadlessInput>',
|
|
28
29
|
);
|
|
30
|
+
const name = computed(() => input.name ?? undefined);
|
|
29
31
|
const value = computed(() => input.value);
|
|
30
32
|
const checked = computed(() => {
|
|
31
33
|
if (props.type !== 'checkbox') {
|
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
<script setup lang="ts">
|
|
6
6
|
import { computed } from 'vue';
|
|
7
7
|
|
|
8
|
-
import Errors from '@/errors/Errors';
|
|
9
8
|
import { requiredObjectProp } from '@/utils/vue';
|
|
9
|
+
import { getErrorMessage } from '@/errors/utils';
|
|
10
10
|
import type { ErrorSource } from '@/errors/Errors.state';
|
|
11
11
|
|
|
12
12
|
import AGMarkdown from './AGMarkdown.vue';
|
|
13
13
|
|
|
14
14
|
const props = defineProps({ error: requiredObjectProp<ErrorSource>() });
|
|
15
|
-
const message = computed(() =>
|
|
15
|
+
const message = computed(() => getErrorMessage(props.error));
|
|
16
16
|
</script>
|
package/src/directives/index.ts
CHANGED
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
import { defineDirective } from '@/utils/vue';
|
|
2
2
|
|
|
3
|
+
export interface ElementSize {
|
|
4
|
+
width: number;
|
|
5
|
+
height: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export type MeasureDirectiveListener = (size: ElementSize) => unknown;
|
|
9
|
+
|
|
3
10
|
export default defineDirective({
|
|
4
|
-
mounted(element: HTMLElement, { value }
|
|
11
|
+
mounted(element: HTMLElement, { value }) {
|
|
12
|
+
const listener = typeof value === 'function' ? (value as MeasureDirectiveListener) : null;
|
|
5
13
|
const sizes = element.getBoundingClientRect();
|
|
6
14
|
|
|
15
|
+
// TODO guard with modifiers.css once typed properly
|
|
7
16
|
element.style.setProperty('--width', `${sizes.width}px`);
|
|
8
17
|
element.style.setProperty('--height', `${sizes.height}px`);
|
|
9
18
|
|
|
10
|
-
|
|
19
|
+
listener?.({ width: sizes.width, height: sizes.height });
|
|
11
20
|
},
|
|
12
21
|
});
|
package/src/errors/Errors.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { translateWithDefault } from '@/lang/utils';
|
|
|
7
7
|
|
|
8
8
|
import Service from './Errors.state';
|
|
9
9
|
import { Colors } from '@/components/constants';
|
|
10
|
+
import { Events } from '@/services';
|
|
10
11
|
import type { AGErrorReportModalProps } from '@/components/modals/AGErrorReportModal';
|
|
11
12
|
import type { ErrorReport, ErrorReportLog, ErrorSource } from './Errors.state';
|
|
12
13
|
import type { ModalComponent } from '@/ui/UI.state';
|
|
@@ -39,6 +40,8 @@ export class ErrorsService extends Service {
|
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
public async report(error: ErrorSource, message?: string): Promise<void> {
|
|
43
|
+
await Events.emit('error', { error, message });
|
|
44
|
+
|
|
42
45
|
if (App.testing) {
|
|
43
46
|
throw error;
|
|
44
47
|
}
|
|
@@ -114,22 +117,6 @@ export class ErrorsService extends Service {
|
|
|
114
117
|
});
|
|
115
118
|
}
|
|
116
119
|
|
|
117
|
-
public getErrorMessage(error: ErrorSource): string {
|
|
118
|
-
if (typeof error === 'string') {
|
|
119
|
-
return error;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
if (error instanceof Error || error instanceof JSError) {
|
|
123
|
-
return error.message;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
if (isObject(error)) {
|
|
127
|
-
return toString(error['message'] ?? error['description'] ?? 'Unknown error object');
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return translateWithDefault('errors.unknown', 'Unknown Error');
|
|
131
|
-
}
|
|
132
|
-
|
|
133
120
|
private logError(error: unknown): void {
|
|
134
121
|
// eslint-disable-next-line no-console
|
|
135
122
|
console.error(error);
|
|
@@ -190,3 +177,9 @@ export class ErrorsService extends Service {
|
|
|
190
177
|
}
|
|
191
178
|
|
|
192
179
|
export default facade(ErrorsService);
|
|
180
|
+
|
|
181
|
+
declare module '@/services/Events' {
|
|
182
|
+
export interface EventsPayload {
|
|
183
|
+
error: { error: ErrorSource; message?: string };
|
|
184
|
+
}
|
|
185
|
+
}
|
package/src/errors/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { definePlugin } from '@/plugins';
|
|
|
6
6
|
import Errors from './Errors';
|
|
7
7
|
import { ErrorReport, ErrorReportLog, ErrorSource } from './Errors.state';
|
|
8
8
|
|
|
9
|
+
export * from './utils';
|
|
9
10
|
export { Errors, ErrorSource, ErrorReport, ErrorReportLog };
|
|
10
11
|
|
|
11
12
|
const services = { $errors: Errors };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { JSError, isObject, toString } from '@noeldemartin/utils';
|
|
2
|
+
import { translateWithDefault } from '@/lang/utils';
|
|
3
|
+
import type { ErrorSource } from './Errors.state';
|
|
4
|
+
|
|
5
|
+
export function getErrorMessage(error: ErrorSource): string {
|
|
6
|
+
if (typeof error === 'string') {
|
|
7
|
+
return error;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (error instanceof Error || error instanceof JSError) {
|
|
11
|
+
return error.message;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (isObject(error)) {
|
|
15
|
+
return toString(error['message'] ?? error['description'] ?? 'Unknown error object');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return translateWithDefault('errors.unknown', 'Unknown Error');
|
|
19
|
+
}
|
package/src/lang/Lang.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { facade
|
|
1
|
+
import { facade } from '@noeldemartin/utils';
|
|
2
2
|
|
|
3
3
|
import App from '@/services/App';
|
|
4
4
|
import Service from '@/services/Service';
|
|
5
5
|
|
|
6
6
|
export interface LangProvider {
|
|
7
7
|
translate(key: string, parameters?: Record<string, unknown>): string;
|
|
8
|
+
translateWithDefault(key: string, defaultMessage: string, parameters?: Record<string, unknown>): string;
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
export class LangService extends Service {
|
|
@@ -21,6 +22,12 @@ export class LangService extends Service {
|
|
|
21
22
|
|
|
22
23
|
return key;
|
|
23
24
|
},
|
|
25
|
+
translateWithDefault: (_, defaultMessage) => {
|
|
26
|
+
// eslint-disable-next-line no-console
|
|
27
|
+
App.development && console.warn('Lang provider is missing');
|
|
28
|
+
|
|
29
|
+
return defaultMessage;
|
|
30
|
+
},
|
|
24
31
|
};
|
|
25
32
|
}
|
|
26
33
|
|
|
@@ -32,27 +39,8 @@ export class LangService extends Service {
|
|
|
32
39
|
return this.provider.translate(key, parameters) ?? key;
|
|
33
40
|
}
|
|
34
41
|
|
|
35
|
-
public translateWithDefault(key: string, defaultMessage: string): string
|
|
36
|
-
|
|
37
|
-
public translateWithDefault(
|
|
38
|
-
key: string,
|
|
39
|
-
defaultMessageOrParameters?: string | Record<string, unknown>,
|
|
40
|
-
defaultMessage?: string,
|
|
41
|
-
): string {
|
|
42
|
-
defaultMessage ??= defaultMessageOrParameters as string;
|
|
43
|
-
|
|
44
|
-
const parameters = typeof defaultMessageOrParameters === 'string' ? {} : defaultMessageOrParameters ?? {};
|
|
45
|
-
const message = this.provider.translate(key, parameters) ?? key;
|
|
46
|
-
|
|
47
|
-
if (message === key) {
|
|
48
|
-
return Object.entries(parameters).reduce(
|
|
49
|
-
(renderedMessage, [name, value]) =>
|
|
50
|
-
renderedMessage.replace(new RegExp(`\\{\\s*${name}\\s*\\}`, 'g'), toString(value)),
|
|
51
|
-
defaultMessage,
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return message;
|
|
42
|
+
public translateWithDefault(key: string, defaultMessage: string, parameters: Record<string, unknown> = {}): string {
|
|
43
|
+
return this.provider.translateWithDefault(key, defaultMessage, parameters);
|
|
56
44
|
}
|
|
57
45
|
|
|
58
46
|
}
|
package/src/main.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
export * from './bootstrap';
|
|
2
2
|
export * from './components';
|
|
3
|
+
export * from './directives';
|
|
3
4
|
export * from './errors';
|
|
4
5
|
export * from './forms';
|
|
5
6
|
export * from './jobs';
|
|
6
7
|
export * from './lang';
|
|
7
8
|
export * from './plugins';
|
|
8
9
|
export * from './services';
|
|
10
|
+
export * from './testing';
|
|
9
11
|
export * from './ui';
|
|
10
12
|
export * from './utils';
|
package/src/services/Events.ts
CHANGED
|
@@ -6,6 +6,8 @@ export interface EventsPayload {}
|
|
|
6
6
|
export interface EventListenerOptions {
|
|
7
7
|
priority: number;
|
|
8
8
|
}
|
|
9
|
+
export type AerogelGlobalEvents = Partial<{ [Event in EventWithoutPayload]: () => unknown }> &
|
|
10
|
+
Partial<{ [Event in EventWithPayload]: EventListener<EventsPayload[Event]> }>;
|
|
9
11
|
|
|
10
12
|
export type EventListener<T = unknown> = (payload: T) => unknown;
|
|
11
13
|
export type UnknownEvent<T> = T extends keyof EventsPayload ? never : T;
|
|
@@ -28,6 +30,11 @@ export class EventsService extends Service {
|
|
|
28
30
|
|
|
29
31
|
private listeners: Record<string, { priorities: number[]; handlers: Record<number, EventListener[]> }> = {};
|
|
30
32
|
|
|
33
|
+
protected async boot(): Promise<void> {
|
|
34
|
+
Object.entries(globalThis.__aerogelEvents__ ?? {}).forEach(([event, listener]) =>
|
|
35
|
+
this.on(event as string, listener as EventListener));
|
|
36
|
+
}
|
|
37
|
+
|
|
31
38
|
public emit<Event extends EventWithoutPayload>(event: Event): Promise<void>;
|
|
32
39
|
public emit<Event extends EventWithPayload>(event: Event, payload: EventsPayload[Event]): Promise<void>;
|
|
33
40
|
public emit<Event extends string>(event: UnknownEvent<Event>, payload?: unknown): Promise<void>;
|
|
@@ -141,3 +148,8 @@ export class EventsService extends Service {
|
|
|
141
148
|
}
|
|
142
149
|
|
|
143
150
|
export default facade(EventsService);
|
|
151
|
+
|
|
152
|
+
declare global {
|
|
153
|
+
// eslint-disable-next-line no-var
|
|
154
|
+
var __aerogelEvents__: AerogelGlobalEvents | undefined;
|
|
155
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { GetClosureArgs } from '@noeldemartin/utils';
|
|
2
|
+
|
|
3
|
+
import Events from '@/services/Events';
|
|
4
|
+
import { definePlugin } from '@/plugins';
|
|
5
|
+
|
|
6
|
+
export interface AerogelTestingRuntime {
|
|
7
|
+
on: (typeof Events)['on'];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default definePlugin({
|
|
11
|
+
async install() {
|
|
12
|
+
if (import.meta.env.MODE !== 'testing') {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
globalThis.testingRuntime = {
|
|
17
|
+
on: ((...args: GetClosureArgs<(typeof Events)['on']>) => Events.on(...args)) as (typeof Events)['on'],
|
|
18
|
+
};
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
declare global {
|
|
23
|
+
// eslint-disable-next-line no-var
|
|
24
|
+
var testingRuntime: AerogelTestingRuntime | undefined;
|
|
25
|
+
}
|
package/src/ui/UI.ts
CHANGED
|
@@ -167,7 +167,7 @@ export class UIService extends Service {
|
|
|
167
167
|
const snackbar: Snackbar = {
|
|
168
168
|
id: uuid(),
|
|
169
169
|
properties: { message, ...options },
|
|
170
|
-
component: options.component ??
|
|
170
|
+
component: markRaw(options.component ?? this.requireComponent(UIComponents.Snackbar)),
|
|
171
171
|
};
|
|
172
172
|
|
|
173
173
|
this.setState('snackbars', this.snackbars.concat(snackbar));
|
|
@@ -274,10 +274,12 @@ export default facade(UIService);
|
|
|
274
274
|
|
|
275
275
|
declare module '@/services/Events' {
|
|
276
276
|
export interface EventsPayload {
|
|
277
|
-
'modal-will-close': { modal: Modal; result?: unknown };
|
|
278
|
-
'modal-closed': { modal: Modal; result?: unknown };
|
|
279
277
|
'close-modal': { id: string; result?: unknown };
|
|
280
278
|
'hide-modal': { id: string };
|
|
279
|
+
'hide-overlays-backdrop': void;
|
|
280
|
+
'modal-closed': { modal: Modal; result?: unknown };
|
|
281
|
+
'modal-will-close': { modal: Modal; result?: unknown };
|
|
281
282
|
'show-modal': { id: string };
|
|
283
|
+
'show-overlays-backdrop': void;
|
|
282
284
|
}
|
|
283
285
|
}
|