@antelopejs/dms 0.4.8 → 0.5.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.
- package/CHANGELOG.md +32 -0
- package/dist/hooks/invite-extensions.js +0 -2
- package/dist/hooks/invite-extensions.js.map +1 -1
- package/dist/implementations/dms/page.js +6 -7
- package/dist/implementations/dms/page.js.map +1 -1
- package/dist/implementations/dms/realtime.d.ts +2 -2
- package/dist/implementations/dms/realtime.js +9 -3
- package/dist/implementations/dms/realtime.js.map +1 -1
- package/dist/implementations/dms-base/table-view.d.ts +5 -0
- package/dist/implementations/dms-base/table-view.js +7 -1
- package/dist/implementations/dms-base/table-view.js.map +1 -1
- package/dist/realtime/index.js +0 -2
- package/dist/realtime/index.js.map +1 -1
- package/dist/realtime/registry.d.ts +1 -0
- package/dist/realtime/registry.js +9 -0
- package/dist/realtime/registry.js.map +1 -1
- package/dist/realtime/table-view.d.ts +3 -0
- package/dist/realtime/table-view.js +25 -0
- package/dist/realtime/table-view.js.map +1 -0
- package/dist/test/antelope.test.js +6 -0
- package/dist/test/antelope.test.js.map +1 -1
- package/dist/test/helpers/reload-host.d.ts +9 -0
- package/dist/test/helpers/reload-host.js +21 -0
- package/dist/test/helpers/reload-host.js.map +1 -0
- package/dist/test/integration/invite-resolution.test.js +0 -2
- package/dist/test/integration/invite-resolution.test.js.map +1 -1
- package/dist/test/reload-host/index.d.ts +2 -0
- package/dist/test/reload-host/index.js +32 -0
- package/dist/test/reload-host/index.js.map +1 -0
- package/dist/test/reload-host/package.json +8 -0
- package/dist/test/unit/frontend-renderer-range.test.d.ts +1 -0
- package/dist/test/unit/frontend-renderer-range.test.js +45 -0
- package/dist/test/unit/frontend-renderer-range.test.js.map +1 -0
- package/dist/test/unit/interfaces/dms/invite-extension.test.js +22 -24
- package/dist/test/unit/interfaces/dms/invite-extension.test.js.map +1 -1
- package/dist/test/unit/interfaces/dms/module-reload-registrations.test.d.ts +1 -0
- package/dist/test/unit/interfaces/dms/module-reload-registrations.test.js +51 -0
- package/dist/test/unit/interfaces/dms/module-reload-registrations.test.js.map +1 -0
- package/dist/test/unit/interfaces/dms/page-topics.test.d.ts +1 -0
- package/dist/test/unit/interfaces/dms/page-topics.test.js +194 -0
- package/dist/test/unit/interfaces/dms/page-topics.test.js.map +1 -0
- package/dist/test/unit/interfaces/dms-base/layouts.test.js +16 -0
- package/dist/test/unit/interfaces/dms-base/layouts.test.js.map +1 -1
- package/dist/test/unit/interfaces/dms-base/table-view-evaluation-order.fixture.d.ts +5 -0
- package/dist/test/unit/interfaces/dms-base/table-view-evaluation-order.fixture.js +82 -0
- package/dist/test/unit/interfaces/dms-base/table-view-evaluation-order.fixture.js.map +1 -0
- package/dist/test/unit/interfaces/dms-base/table-view-evaluation-order.test.d.ts +1 -0
- package/dist/test/unit/interfaces/dms-base/table-view-evaluation-order.test.js +24 -0
- package/dist/test/unit/interfaces/dms-base/table-view-evaluation-order.test.js.map +1 -0
- package/dist/test/unit/interfaces/dms-base/table-view-get-guard.test.js +10 -4
- package/dist/test/unit/interfaces/dms-base/table-view-get-guard.test.js.map +1 -1
- package/dist/test/unit/interfaces/dms-base/table-view-realtime.test.d.ts +1 -0
- package/dist/test/unit/interfaces/dms-base/table-view-realtime.test.js +139 -0
- package/dist/test/unit/interfaces/dms-base/table-view-realtime.test.js.map +1 -0
- package/dist/test/unit/utils/owned-registry.test.d.ts +1 -0
- package/dist/test/unit/utils/owned-registry.test.js +61 -0
- package/dist/test/unit/utils/owned-registry.test.js.map +1 -0
- package/dist/validation/auth.schema.d.ts +2 -2
- package/frontend-vue/layers/dms-layout/app/composables/layout/usePageFillHeight.ts +37 -0
- package/frontend-vue/layers/dms-layout/app/custom-layouts/DefaultLayout.vue +9 -0
- package/frontend-vue/layers/dms-layout/app/pages/[...slug].vue +16 -1
- package/frontend-vue/package.json +4 -1
- package/frontend-vue/pnpm-lock.yaml +8 -8
- package/frontend-vue/tests/page-fill-height.test.ts +352 -0
- package/package.json +4 -4
- package/skills/dms-module/REFERENCE.md +2 -1
- package/skills/dms-module/SKILL.md +5 -0
- package/dist/realtime/tableview-bridge.d.ts +0 -1
- package/dist/realtime/tableview-bridge.js +0 -38
- package/dist/realtime/tableview-bridge.js.map +0 -1
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const modules_1 = require("@antelopejs/interface-core/modules");
|
|
4
|
+
const owned_registry_1 = require("@antelopejs/interface-dms/utils/owned-registry");
|
|
5
|
+
const chai_1 = require("chai");
|
|
6
|
+
function generation(module, index) {
|
|
7
|
+
return { module, owner: `${module}#${index}` };
|
|
8
|
+
}
|
|
9
|
+
function addAs(context, registry, value) {
|
|
10
|
+
(0, modules_1.RunWithModuleContext)(context, () => registry.add(value));
|
|
11
|
+
}
|
|
12
|
+
function destroy(context) {
|
|
13
|
+
(0, modules_1.RunWithModuleContext)(context, () => {
|
|
14
|
+
modules_1.Events.ModuleDestroyed.emit(context.module);
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
describe("[unit] utils/owned-registry", () => {
|
|
18
|
+
it("releases what a generation added when it is destroyed, and only that", () => {
|
|
19
|
+
const registry = new owned_registry_1.OwnedRegistry();
|
|
20
|
+
const reloaded = generation("owned-registry-reloaded", 1);
|
|
21
|
+
const other = generation("owned-registry-other", 1);
|
|
22
|
+
addAs(reloaded, registry, "reloaded");
|
|
23
|
+
addAs(other, registry, "other");
|
|
24
|
+
destroy(reloaded);
|
|
25
|
+
(0, chai_1.expect)(registry.values()).to.deep.equal(["other"]);
|
|
26
|
+
destroy(other);
|
|
27
|
+
(0, chai_1.expect)(registry.values()).to.deep.equal([]);
|
|
28
|
+
});
|
|
29
|
+
it("keeps what the next generation of the same module adds", () => {
|
|
30
|
+
const registry = new owned_registry_1.OwnedRegistry();
|
|
31
|
+
const first = generation("owned-registry-next", 1);
|
|
32
|
+
const next = generation("owned-registry-next", 2);
|
|
33
|
+
addAs(first, registry, "first");
|
|
34
|
+
destroy(first);
|
|
35
|
+
addAs(next, registry, "next");
|
|
36
|
+
(0, chai_1.expect)(registry.values()).to.deep.equal(["next"]);
|
|
37
|
+
destroy(next);
|
|
38
|
+
});
|
|
39
|
+
it("lists the live values in the order they were added", () => {
|
|
40
|
+
const registry = new owned_registry_1.OwnedRegistry();
|
|
41
|
+
const owner = generation("owned-registry-order", 1);
|
|
42
|
+
for (const value of ["first", "second", "third"]) {
|
|
43
|
+
addAs(owner, registry, value);
|
|
44
|
+
}
|
|
45
|
+
(0, chai_1.expect)(registry.values()).to.deep.equal(["first", "second", "third"]);
|
|
46
|
+
destroy(owner);
|
|
47
|
+
});
|
|
48
|
+
it("removes only the oldest registration a matcher accepts", () => {
|
|
49
|
+
const registry = new owned_registry_1.OwnedRegistry();
|
|
50
|
+
const owner = generation("owned-registry-remove", 1);
|
|
51
|
+
for (const value of ["twice", "once", "twice"]) {
|
|
52
|
+
addAs(owner, registry, value);
|
|
53
|
+
}
|
|
54
|
+
registry.remove((value) => value === "twice");
|
|
55
|
+
(0, chai_1.expect)(registry.values()).to.deep.equal(["once", "twice"]);
|
|
56
|
+
(0, chai_1.expect)(registry.has((value) => value === "once")).to.equal(true);
|
|
57
|
+
(0, chai_1.expect)(registry.has((value) => value === "never")).to.equal(false);
|
|
58
|
+
destroy(owner);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
//# sourceMappingURL=owned-registry.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"owned-registry.test.js","sourceRoot":"","sources":["../../../../src/test/unit/utils/owned-registry.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -42,14 +42,14 @@ export declare const authSchema: {
|
|
|
42
42
|
code: string;
|
|
43
43
|
state: string;
|
|
44
44
|
state_cookie: string;
|
|
45
|
-
invite?: string | undefined;
|
|
46
45
|
language?: string | undefined;
|
|
46
|
+
invite?: string | undefined;
|
|
47
47
|
}, {
|
|
48
48
|
code: string;
|
|
49
49
|
state: string;
|
|
50
50
|
state_cookie: string;
|
|
51
|
-
invite?: string | undefined;
|
|
52
51
|
language?: string | undefined;
|
|
52
|
+
invite?: string | undefined;
|
|
53
53
|
}>;
|
|
54
54
|
refresh: z.ZodObject<{
|
|
55
55
|
token: z.ZodString;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { InjectionKey, Ref } from "vue";
|
|
2
|
+
|
|
3
|
+
const PAGE_FILL_HEIGHT_KEY: InjectionKey<Readonly<Ref<boolean>>> = Symbol(
|
|
4
|
+
"dms:page-fill-height",
|
|
5
|
+
);
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The flex column a page filling the panel (`DefaultLayout({ fillHeight })`)
|
|
9
|
+
* is laid out in, from the scrolling panel body down to the component that
|
|
10
|
+
* takes the height left.
|
|
11
|
+
*/
|
|
12
|
+
export const PAGE_FILL_HEIGHT_CLASSES = {
|
|
13
|
+
/**
|
|
14
|
+
* The page region, the panel body's only child. The minimum height sits
|
|
15
|
+
* here rather than on the component that fills: a floor set lower down
|
|
16
|
+
* overflows this region, and the panel then scrolls past its own bottom
|
|
17
|
+
* padding.
|
|
18
|
+
*/
|
|
19
|
+
region: "flex min-h-[30rem] flex-1 flex-col",
|
|
20
|
+
/** The page stack, and the component wrapper that takes the height left. */
|
|
21
|
+
column: "flex min-h-0 flex-1 flex-col",
|
|
22
|
+
} as const;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Lets the page rendered in a layout's slot know whether the layout gives it
|
|
26
|
+
* the height of the panel.
|
|
27
|
+
*/
|
|
28
|
+
export const providePageFillHeight = (fillHeight: Readonly<Ref<boolean>>) => {
|
|
29
|
+
provide(PAGE_FILL_HEIGHT_KEY, fillHeight);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Whether the layout rendering the page asked it to fill the panel height.
|
|
34
|
+
* `false` under a layout that does not provide it.
|
|
35
|
+
*/
|
|
36
|
+
export const usePageFillHeight = (): Readonly<Ref<boolean>> =>
|
|
37
|
+
inject(PAGE_FILL_HEIGHT_KEY, () => ref(false), true);
|
|
@@ -8,6 +8,7 @@ import DashboardBanners from "../build/components/layout/DashboardBanners.vue";
|
|
|
8
8
|
interface Props {
|
|
9
9
|
fullWidth?: boolean;
|
|
10
10
|
hideHeader?: boolean;
|
|
11
|
+
fillHeight?: boolean;
|
|
11
12
|
icon?: string;
|
|
12
13
|
title?: string;
|
|
13
14
|
description?: string;
|
|
@@ -16,10 +17,17 @@ interface Props {
|
|
|
16
17
|
const props = withDefaults(defineProps<Props>(), {
|
|
17
18
|
fullWidth: true,
|
|
18
19
|
hideHeader: false,
|
|
20
|
+
fillHeight: false,
|
|
19
21
|
icon: "i-ph-file",
|
|
20
22
|
title: undefined,
|
|
21
23
|
description: undefined,
|
|
22
24
|
});
|
|
25
|
+
|
|
26
|
+
providePageFillHeight(toRef(props, "fillHeight"));
|
|
27
|
+
|
|
28
|
+
const regionClass = computed(() =>
|
|
29
|
+
props.fillHeight ? PAGE_FILL_HEIGHT_CLASSES.region : undefined,
|
|
30
|
+
);
|
|
23
31
|
</script>
|
|
24
32
|
|
|
25
33
|
<template>
|
|
@@ -37,6 +45,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
37
45
|
data-dms-page-region
|
|
38
46
|
data-dms-page-content
|
|
39
47
|
:full-width="props.fullWidth"
|
|
48
|
+
:class="regionClass"
|
|
40
49
|
>
|
|
41
50
|
<PageHeader
|
|
42
51
|
v-if="props.title && !props.hideHeader"
|
|
@@ -339,6 +339,17 @@ const components = computed(() =>
|
|
|
339
339
|
}),
|
|
340
340
|
);
|
|
341
341
|
|
|
342
|
+
// A page filling the panel (`DefaultLayout({ fillHeight })`) gives the height
|
|
343
|
+
// left under its header to its last component; the ones above it keep theirs.
|
|
344
|
+
const fillHeight = usePageFillHeight();
|
|
345
|
+
const fillColumnClass = PAGE_FILL_HEIGHT_CLASSES.column;
|
|
346
|
+
const stackClass = computed(() =>
|
|
347
|
+
fillHeight.value ? fillColumnClass : undefined,
|
|
348
|
+
);
|
|
349
|
+
const fillingComponentId = computed(() =>
|
|
350
|
+
fillHeight.value ? components.value.at(-1)?.id : undefined,
|
|
351
|
+
);
|
|
352
|
+
|
|
342
353
|
// --- Live-edit highlight ---------------------------------------------------
|
|
343
354
|
// When the active page's layout changes in place (DMS dev hot-reload, same
|
|
344
355
|
// route), animate the components that changed. Added/removed components are
|
|
@@ -457,12 +468,16 @@ if (import.meta.env.DEV) {
|
|
|
457
468
|
tag="div"
|
|
458
469
|
name="dms-fresh"
|
|
459
470
|
class="dms-page-stack space-y-6"
|
|
471
|
+
:class="stackClass"
|
|
460
472
|
:css="animateLayout"
|
|
461
473
|
>
|
|
462
474
|
<div
|
|
463
475
|
v-for="component in components"
|
|
464
476
|
:key="component.id"
|
|
465
|
-
:class="
|
|
477
|
+
:class="[
|
|
478
|
+
{ 'dms-fresh-modified': freshComponentIds.has(component.id) },
|
|
479
|
+
component.id === fillingComponentId && fillColumnClass,
|
|
480
|
+
]"
|
|
466
481
|
>
|
|
467
482
|
<DmsRecursiveComponent
|
|
468
483
|
:component="component"
|
|
@@ -10,6 +10,9 @@
|
|
|
10
10
|
},
|
|
11
11
|
"license": "Apache-2.0",
|
|
12
12
|
"packageManager": "pnpm@10.6.5",
|
|
13
|
+
"engines": {
|
|
14
|
+
"@antelopejs/dms-frontend": ">=0.3.1 <0.4.0"
|
|
15
|
+
},
|
|
13
16
|
"scripts": {
|
|
14
17
|
"typecheck": "ajs dms verify-source --layer \"$PWD\" --local-package @antelopejs/dms=.. --local-package @antelopejs/interface-dms=../../interface-dms",
|
|
15
18
|
"test": "vitest run",
|
|
@@ -58,7 +61,7 @@
|
|
|
58
61
|
"zod": "^3.24.2"
|
|
59
62
|
},
|
|
60
63
|
"devDependencies": {
|
|
61
|
-
"@antelopejs/core": ">=1.
|
|
64
|
+
"@antelopejs/core": ">=1.10.1 <2",
|
|
62
65
|
"@antelopejs/dms-frontend": ">=0.3.2 <1.0.0",
|
|
63
66
|
"@eslint/js": "^8.57.0",
|
|
64
67
|
"@inertiajs/vue3": "^3.7.0",
|
|
@@ -121,11 +121,11 @@ importers:
|
|
|
121
121
|
version: 3.25.76
|
|
122
122
|
devDependencies:
|
|
123
123
|
'@antelopejs/core':
|
|
124
|
-
specifier: '>=1.
|
|
125
|
-
version: 1.
|
|
124
|
+
specifier: '>=1.10.1 <2'
|
|
125
|
+
version: 1.10.1(@types/node@24.8.1)
|
|
126
126
|
'@antelopejs/dms-frontend':
|
|
127
127
|
specifier: '>=0.3.2 <1.0.0'
|
|
128
|
-
version: 0.3.2(@antelopejs/core@1.
|
|
128
|
+
version: 0.3.2(@antelopejs/core@1.10.1(@types/node@24.8.1))
|
|
129
129
|
'@eslint/js':
|
|
130
130
|
specifier: ^8.57.0
|
|
131
131
|
version: 8.57.0
|
|
@@ -196,8 +196,8 @@ packages:
|
|
|
196
196
|
resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
|
|
197
197
|
engines: {node: '>=10'}
|
|
198
198
|
|
|
199
|
-
'@antelopejs/core@1.
|
|
200
|
-
resolution: {integrity: sha512-
|
|
199
|
+
'@antelopejs/core@1.10.1':
|
|
200
|
+
resolution: {integrity: sha512-NcC6N8Aq14Ph7GRto0lpdRdgUKZAu3ZHDlAF5hXGFZPKKoNm8Da9i4nyB/NI4Sgx3rbE0/KoqCcHpTtQJgrAYg==}
|
|
201
201
|
hasBin: true
|
|
202
202
|
|
|
203
203
|
'@antelopejs/dms-frontend@0.3.2':
|
|
@@ -4312,7 +4312,7 @@ snapshots:
|
|
|
4312
4312
|
|
|
4313
4313
|
'@alloc/quick-lru@5.2.0': {}
|
|
4314
4314
|
|
|
4315
|
-
'@antelopejs/core@1.
|
|
4315
|
+
'@antelopejs/core@1.10.1(@types/node@24.8.1)':
|
|
4316
4316
|
dependencies:
|
|
4317
4317
|
'@antelopejs/interface-core': 0.1.1
|
|
4318
4318
|
'@types/proper-lockfile': 4.1.4
|
|
@@ -4331,7 +4331,7 @@ snapshots:
|
|
|
4331
4331
|
transitivePeerDependencies:
|
|
4332
4332
|
- '@types/node'
|
|
4333
4333
|
|
|
4334
|
-
'@antelopejs/dms-frontend@0.3.2(@antelopejs/core@1.
|
|
4334
|
+
'@antelopejs/dms-frontend@0.3.2(@antelopejs/core@1.10.1(@types/node@24.8.1))':
|
|
4335
4335
|
dependencies:
|
|
4336
4336
|
boxen: 5.1.2
|
|
4337
4337
|
chalk: 4.1.2
|
|
@@ -4342,7 +4342,7 @@ snapshots:
|
|
|
4342
4342
|
semver: 7.8.5
|
|
4343
4343
|
unzipper: 0.12.5
|
|
4344
4344
|
optionalDependencies:
|
|
4345
|
-
'@antelopejs/core': 1.
|
|
4345
|
+
'@antelopejs/core': 1.10.1(@types/node@24.8.1)
|
|
4346
4346
|
'@oxc-parser/binding-darwin-arm64': 0.95.0
|
|
4347
4347
|
'@oxc-parser/binding-darwin-x64': 0.95.0
|
|
4348
4348
|
'@oxc-parser/binding-linux-arm64-gnu': 0.95.0
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
import {
|
|
3
|
+
computed,
|
|
4
|
+
createApp,
|
|
5
|
+
createSSRApp,
|
|
6
|
+
h,
|
|
7
|
+
inject,
|
|
8
|
+
nextTick,
|
|
9
|
+
onMounted,
|
|
10
|
+
onUnmounted,
|
|
11
|
+
provide,
|
|
12
|
+
reactive,
|
|
13
|
+
ref,
|
|
14
|
+
Suspense,
|
|
15
|
+
toRef,
|
|
16
|
+
watch,
|
|
17
|
+
type App,
|
|
18
|
+
type Component,
|
|
19
|
+
type FunctionalComponent,
|
|
20
|
+
} from "vue";
|
|
21
|
+
import { renderToString } from "vue/server-renderer";
|
|
22
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
23
|
+
import DefaultLayout from "../layers/dms-layout/app/custom-layouts/DefaultLayout.vue";
|
|
24
|
+
import CatchAllPage from "../layers/dms-layout/app/pages/[...slug].vue";
|
|
25
|
+
import {
|
|
26
|
+
PAGE_FILL_HEIGHT_CLASSES,
|
|
27
|
+
providePageFillHeight,
|
|
28
|
+
usePageFillHeight,
|
|
29
|
+
} from "../layers/dms-layout/app/composables/layout/usePageFillHeight";
|
|
30
|
+
|
|
31
|
+
vi.mock(
|
|
32
|
+
"../layers/dms-layout/app/build/components/layout/DashboardSidebar.vue",
|
|
33
|
+
() => ({ default: () => null }),
|
|
34
|
+
);
|
|
35
|
+
vi.mock(
|
|
36
|
+
"../layers/dms-layout/app/build/components/layout/DashboardHeader.vue",
|
|
37
|
+
() => ({ default: () => null }),
|
|
38
|
+
);
|
|
39
|
+
vi.mock(
|
|
40
|
+
"../layers/dms-layout/app/build/components/layout/DashboardBanners.vue",
|
|
41
|
+
() => ({ default: () => null }),
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
interface PageLayoutFixture {
|
|
45
|
+
components: Record<string, { componentName: string }>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const route = reactive({ path: "/tools/explorer", query: {} });
|
|
49
|
+
const pageLayout = ref<PageLayoutFixture | null>(null);
|
|
50
|
+
|
|
51
|
+
// The markup `DefaultLayout` and `pages/[...slug].vue` rendered for a
|
|
52
|
+
// two-component page before `fillHeight` existed, with the stubs below.
|
|
53
|
+
const FLOW_PAGE_MARKUP =
|
|
54
|
+
"<div data-dms-persistent-shell data-group><!---->" +
|
|
55
|
+
"<div data-panel><!--[--><!----><!----><!--]-->" +
|
|
56
|
+
"<div data-body>" +
|
|
57
|
+
'<div class="w-full max-w-none" data-dms-page-region data-dms-page-content><!--[-->' +
|
|
58
|
+
'<section class="flex gap-4 items-start pt-6 pb-7">' +
|
|
59
|
+
'<div class="rounded-lg bg-primary/10 shrink-0 ring ring-primary/20 flex items-center justify-center size-12"><i class="text-primary"></i></div>' +
|
|
60
|
+
'<div class="flex-1 min-w-0">' +
|
|
61
|
+
'<h1 class="text-highlighted text-[22px] font-semibold leading-tight tracking-tight"><!--[-->Explorer<!--]--></h1><!--v-if--></div><!--[--><!--]--></section><!--[-->' +
|
|
62
|
+
'<div class="dms-page-stack space-y-6">' +
|
|
63
|
+
'<div class="">' +
|
|
64
|
+
'<section data-component="stats" page-id="tools.explorer"></section></div>' +
|
|
65
|
+
'<div class="">' +
|
|
66
|
+
'<section data-component="explorer" page-id="tools.explorer"></section></div></div><!--]--><!--]--></div></div><!--[--><!----><!--]--></div></div>';
|
|
67
|
+
|
|
68
|
+
function withComponents(...ids: string[]): PageLayoutFixture {
|
|
69
|
+
return {
|
|
70
|
+
components: Object.fromEntries(
|
|
71
|
+
ids.map((id) => [id, { componentName: `tool-${id}` }]),
|
|
72
|
+
),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Functional stubs hand non-class attributes on only when told to: these two
|
|
77
|
+
// spread them, so the markers the layout sets stay in the markup.
|
|
78
|
+
const Group: FunctionalComponent = (_, { attrs, slots }) =>
|
|
79
|
+
h("div", { ...attrs, "data-group": "" }, slots.default?.());
|
|
80
|
+
Group.inheritAttrs = false;
|
|
81
|
+
|
|
82
|
+
const Panel: FunctionalComponent = (_, { slots }) =>
|
|
83
|
+
h("div", { "data-panel": "" }, [
|
|
84
|
+
slots.header?.(),
|
|
85
|
+
h("div", { "data-body": "" }, slots.body?.()),
|
|
86
|
+
slots.footer?.(),
|
|
87
|
+
]);
|
|
88
|
+
|
|
89
|
+
// Nuxt UI merges its container classes with the ones it is given; the stub
|
|
90
|
+
// keeps the given ones next to one of its own, so a lost class shows.
|
|
91
|
+
const Container: FunctionalComponent = (_, { attrs, slots }) =>
|
|
92
|
+
h("div", { ...attrs, class: ["w-full", attrs.class] }, slots.default?.());
|
|
93
|
+
Container.inheritAttrs = false;
|
|
94
|
+
|
|
95
|
+
const PageComponent: FunctionalComponent<{ componentId: string }> = (props) =>
|
|
96
|
+
h("section", { "data-component": props.componentId });
|
|
97
|
+
PageComponent.props = ["componentId"];
|
|
98
|
+
|
|
99
|
+
const FillHeightProbe: FunctionalComponent = () =>
|
|
100
|
+
h("output", String(usePageFillHeight().value));
|
|
101
|
+
|
|
102
|
+
function installRuntime(): void {
|
|
103
|
+
const runtime: Record<string, unknown> = {
|
|
104
|
+
computed,
|
|
105
|
+
inject,
|
|
106
|
+
onMounted,
|
|
107
|
+
onUnmounted,
|
|
108
|
+
provide,
|
|
109
|
+
ref,
|
|
110
|
+
toRef,
|
|
111
|
+
watch,
|
|
112
|
+
HTTP_FORBIDDEN: 403,
|
|
113
|
+
HTTP_INTERNAL_SERVER_ERROR: 500,
|
|
114
|
+
HTTP_NOT_FOUND: 404,
|
|
115
|
+
PAGE_FILL_HEIGHT_CLASSES,
|
|
116
|
+
providePageFillHeight,
|
|
117
|
+
usePageFillHeight,
|
|
118
|
+
createError: vi.fn(),
|
|
119
|
+
defineDmsPageMeta: vi.fn(),
|
|
120
|
+
firstAccessiblePagePath: vi.fn(),
|
|
121
|
+
navigateDms: vi.fn(),
|
|
122
|
+
preloadPageLayoutComponents: vi.fn(),
|
|
123
|
+
providePageRealtime: vi.fn(),
|
|
124
|
+
resolveDmsComponent: () => "div",
|
|
125
|
+
runCustomValidation: vi.fn(),
|
|
126
|
+
showError: vi.fn(),
|
|
127
|
+
validateRequiredQueryParams: vi.fn(),
|
|
128
|
+
useDefinedFunctions: () => ({ getFunction: () => undefined }),
|
|
129
|
+
useDevReloading: () => ref(false),
|
|
130
|
+
useDmsRoute: () => route,
|
|
131
|
+
useHomepage: () => "/",
|
|
132
|
+
usePageLayout: async () => ({ pageLayout }),
|
|
133
|
+
usePageRealtime: () => ({}),
|
|
134
|
+
usePermissions: () => ({
|
|
135
|
+
permissions: ref([]),
|
|
136
|
+
refreshIfStale: async () => {},
|
|
137
|
+
}),
|
|
138
|
+
useSessionRecovery: () => ({
|
|
139
|
+
loggedIn: ref(true),
|
|
140
|
+
reconcileSession: async () => {},
|
|
141
|
+
redirectToAuth: async () => {},
|
|
142
|
+
}),
|
|
143
|
+
useSiteLayout: () => ({
|
|
144
|
+
siteLayout: ref({ pages: {} }),
|
|
145
|
+
siteLayoutTree: ref([]),
|
|
146
|
+
loadingError: ref(null),
|
|
147
|
+
loadSiteLayout: async () => {},
|
|
148
|
+
refresh: async () => {},
|
|
149
|
+
refreshIfStale: async () => {},
|
|
150
|
+
findMatchingRoute: () => ({
|
|
151
|
+
metadata: { fullId: "tools.explorer", hasAccess: true },
|
|
152
|
+
params: {},
|
|
153
|
+
}),
|
|
154
|
+
}),
|
|
155
|
+
useTranslation: () => ({ processI18n: (value: string) => value }),
|
|
156
|
+
};
|
|
157
|
+
Object.entries(runtime).forEach(([name, value]) =>
|
|
158
|
+
vi.stubGlobal(name, value),
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function withStubs(app: App): App {
|
|
163
|
+
app.component("UDashboardGroup", Group);
|
|
164
|
+
app.component("UDashboardPanel", Panel);
|
|
165
|
+
app.component("UContainer", Container);
|
|
166
|
+
app.component("UIcon", () => h("i"));
|
|
167
|
+
app.component("DmsAppWidgetsDock", () => null);
|
|
168
|
+
app.component("DmsRecursiveComponent", PageComponent);
|
|
169
|
+
return app;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function renderInLayout(
|
|
173
|
+
layoutProps: Record<string, unknown>,
|
|
174
|
+
page: Component,
|
|
175
|
+
): Promise<string> {
|
|
176
|
+
const root = () =>
|
|
177
|
+
h(
|
|
178
|
+
DefaultLayout,
|
|
179
|
+
{ title: "Explorer", ...layoutProps },
|
|
180
|
+
{ default: () => h(page) },
|
|
181
|
+
);
|
|
182
|
+
return renderToString(withStubs(createSSRApp(root)));
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function parse(markup: string): HTMLElement {
|
|
186
|
+
const host = document.createElement("div");
|
|
187
|
+
host.innerHTML = markup;
|
|
188
|
+
return host;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function classesOf(element: Element | null | undefined): string[] {
|
|
192
|
+
return element?.getAttribute("class")?.split(" ").filter(Boolean) ?? [];
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function expectClasses(element: Element | null | undefined, classes: string) {
|
|
196
|
+
expect(classesOf(element)).toEqual(
|
|
197
|
+
expect.arrayContaining(classes.split(" ")),
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function expectNoFillClasses(element: Element | null | undefined) {
|
|
202
|
+
const fillClasses = new Set(
|
|
203
|
+
`${PAGE_FILL_HEIGHT_CLASSES.region} ${PAGE_FILL_HEIGHT_CLASSES.column}`.split(
|
|
204
|
+
" ",
|
|
205
|
+
),
|
|
206
|
+
);
|
|
207
|
+
expect(classesOf(element).filter((name) => fillClasses.has(name))).toEqual(
|
|
208
|
+
[],
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
beforeEach(() => {
|
|
213
|
+
installRuntime();
|
|
214
|
+
route.path = "/tools/explorer";
|
|
215
|
+
pageLayout.value = withComponents("stats", "explorer");
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
afterEach(() => {
|
|
219
|
+
vi.unstubAllGlobals();
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
describe("usePageFillHeight", () => {
|
|
223
|
+
it("reads false under a layout that does not provide it", async () => {
|
|
224
|
+
const markup = await renderToString(createSSRApp(FillHeightProbe));
|
|
225
|
+
expect(markup).toBe("<output>false</output>");
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
it("follows the layout that provides it", async () => {
|
|
229
|
+
const fillHeight = ref(true);
|
|
230
|
+
const app = withStubs(
|
|
231
|
+
createApp(() =>
|
|
232
|
+
h(
|
|
233
|
+
DefaultLayout,
|
|
234
|
+
{ fillHeight: fillHeight.value },
|
|
235
|
+
{ default: () => h(FillHeightProbe) },
|
|
236
|
+
),
|
|
237
|
+
),
|
|
238
|
+
);
|
|
239
|
+
const host = document.createElement("div");
|
|
240
|
+
app.mount(host);
|
|
241
|
+
expect(host.querySelector("output")?.textContent).toBe("true");
|
|
242
|
+
fillHeight.value = false;
|
|
243
|
+
await nextTick();
|
|
244
|
+
expect(host.querySelector("output")?.textContent).toBe("false");
|
|
245
|
+
app.unmount();
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
describe("DefaultLayout without fillHeight", () => {
|
|
250
|
+
it("renders the page exactly as it did before the option existed", async () => {
|
|
251
|
+
expect(await renderInLayout({}, CatchAllPage)).toBe(FLOW_PAGE_MARKUP);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it("tells the page it does not fill the panel", async () => {
|
|
255
|
+
const markup = await renderInLayout({}, FillHeightProbe);
|
|
256
|
+
expect(parse(markup).querySelector("output")?.textContent).toBe("false");
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
describe("DefaultLayout with fillHeight", () => {
|
|
261
|
+
it("makes the page region a flex column with a minimum height", async () => {
|
|
262
|
+
const host = parse(
|
|
263
|
+
await renderInLayout({ fillHeight: true }, CatchAllPage),
|
|
264
|
+
);
|
|
265
|
+
const region = host.querySelector("[data-dms-page-content]");
|
|
266
|
+
expectClasses(region, PAGE_FILL_HEIGHT_CLASSES.region);
|
|
267
|
+
expectClasses(region, "w-full max-w-none");
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it("keeps the page header a plain item of that column", async () => {
|
|
271
|
+
const host = parse(
|
|
272
|
+
await renderInLayout({ fillHeight: true }, CatchAllPage),
|
|
273
|
+
);
|
|
274
|
+
const header = host.querySelector("[data-dms-page-content] > section");
|
|
275
|
+
expect(classesOf(header)).toEqual([
|
|
276
|
+
"flex",
|
|
277
|
+
"gap-4",
|
|
278
|
+
"items-start",
|
|
279
|
+
"pt-6",
|
|
280
|
+
"pb-7",
|
|
281
|
+
]);
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
it("gives the height left to the last component only", async () => {
|
|
285
|
+
const host = parse(
|
|
286
|
+
await renderInLayout({ fillHeight: true }, CatchAllPage),
|
|
287
|
+
);
|
|
288
|
+
const stack = host.querySelector(".dms-page-stack");
|
|
289
|
+
const [stats, explorer] = Array.from(stack?.children ?? []);
|
|
290
|
+
expectClasses(
|
|
291
|
+
stack,
|
|
292
|
+
`dms-page-stack space-y-6 ${PAGE_FILL_HEIGHT_CLASSES.column}`,
|
|
293
|
+
);
|
|
294
|
+
expectNoFillClasses(stats);
|
|
295
|
+
expectClasses(explorer, PAGE_FILL_HEIGHT_CLASSES.column);
|
|
296
|
+
expect(
|
|
297
|
+
explorer
|
|
298
|
+
?.querySelector("[data-component]")
|
|
299
|
+
?.getAttribute("data-component"),
|
|
300
|
+
).toBe("explorer");
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it("fills with the only component of a single-component page", async () => {
|
|
304
|
+
pageLayout.value = withComponents("explorer");
|
|
305
|
+
const host = parse(
|
|
306
|
+
await renderInLayout({ fillHeight: true }, CatchAllPage),
|
|
307
|
+
);
|
|
308
|
+
const wrappers = host.querySelectorAll(".dms-page-stack > div");
|
|
309
|
+
expect(wrappers).toHaveLength(1);
|
|
310
|
+
expectClasses(wrappers[0], PAGE_FILL_HEIGHT_CLASSES.column);
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
it("tells a custom page, rendered straight in the slot, to fill", async () => {
|
|
314
|
+
const markup = await renderInLayout({ fillHeight: true }, FillHeightProbe);
|
|
315
|
+
expect(parse(markup).querySelector("output")?.textContent).toBe("true");
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
it("drops the fill classes when a navigation lands on a page without the option", async () => {
|
|
319
|
+
const fillHeight = ref(true);
|
|
320
|
+
const app = withStubs(
|
|
321
|
+
createApp(() =>
|
|
322
|
+
h(
|
|
323
|
+
DefaultLayout,
|
|
324
|
+
{ title: "Explorer", fillHeight: fillHeight.value },
|
|
325
|
+
{
|
|
326
|
+
default: () =>
|
|
327
|
+
h(Suspense, null, { default: () => h(CatchAllPage) }),
|
|
328
|
+
},
|
|
329
|
+
),
|
|
330
|
+
),
|
|
331
|
+
);
|
|
332
|
+
const host = document.createElement("div");
|
|
333
|
+
app.mount(host);
|
|
334
|
+
await vi.waitFor(() =>
|
|
335
|
+
expect(host.querySelector(".dms-page-stack")).not.toBeNull(),
|
|
336
|
+
);
|
|
337
|
+
expectClasses(
|
|
338
|
+
host.querySelector(".dms-page-stack"),
|
|
339
|
+
PAGE_FILL_HEIGHT_CLASSES.column,
|
|
340
|
+
);
|
|
341
|
+
|
|
342
|
+
fillHeight.value = false;
|
|
343
|
+
await nextTick();
|
|
344
|
+
|
|
345
|
+
expectNoFillClasses(host.querySelector("[data-dms-page-content]"));
|
|
346
|
+
expectNoFillClasses(host.querySelector(".dms-page-stack"));
|
|
347
|
+
host
|
|
348
|
+
.querySelectorAll(".dms-page-stack > div")
|
|
349
|
+
.forEach((wrapper) => expectNoFillClasses(wrapper));
|
|
350
|
+
app.unmount();
|
|
351
|
+
});
|
|
352
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antelopejs/dms",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/AntelopeJS/dms.git"
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"@antelopejs/interface-data-api": ">=0.1.9 <1.0.0",
|
|
37
37
|
"@antelopejs/interface-database": ">=0.1.8 <1.0.0",
|
|
38
38
|
"@antelopejs/interface-database-decorators": ">=0.1.7 <1.0.0",
|
|
39
|
-
"@antelopejs/interface-dms": ">=0.
|
|
39
|
+
"@antelopejs/interface-dms": ">=0.3.1 <0.4.0",
|
|
40
40
|
"@antelopejs/interface-email": ">=0.0.5 <1.0.0",
|
|
41
41
|
"@antelopejs/interface-file-storage": ">=0.1.3 <1.0.0",
|
|
42
42
|
"archiver": "^7.0.1",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"zod": "~3.24.2"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
|
-
"@antelopejs/core": ">=1.
|
|
55
|
+
"@antelopejs/core": ">=1.10.1 <2",
|
|
56
56
|
"@antelopejs/tooling-configs": ">=0.0.6 <1.0.0",
|
|
57
57
|
"@types/archiver": "^6.0.4",
|
|
58
58
|
"@types/chai": "^4.3.20",
|
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
"@antelopejs/interface-redis": ">=0.0.8 <1.0.0"
|
|
95
95
|
},
|
|
96
96
|
"peerDependencies": {
|
|
97
|
-
"@antelopejs/core": ">=1.
|
|
97
|
+
"@antelopejs/core": ">=1.10.1 <2"
|
|
98
98
|
},
|
|
99
99
|
"peerDependenciesMeta": {
|
|
100
100
|
"@antelopejs/core": {
|
|
@@ -19,7 +19,8 @@ first-party modules follow `dms-<name>` purely for discoverability.
|
|
|
19
19
|
pages/ # @RegisterPage classes + category.ts (see dms-pages)
|
|
20
20
|
routes/ # HTTP controllers feeding the components
|
|
21
21
|
implementations/<name>/index.ts # implementation of the module's own interface
|
|
22
|
-
frontend-vue/ # Vue module: dms.frontend.ts, app/, i18n/locales
|
|
22
|
+
frontend-vue/ # Vue module: dms.frontend.ts, app/, i18n/locales/,
|
|
23
|
+
# package.json (engines: the loader releases it supports)
|
|
23
24
|
package.json / tsconfig.json
|
|
24
25
|
interface-<name>/ # the module's OWN interface, its own published package
|
|
25
26
|
src/index.ts # (rare — many modules ship none)
|
|
@@ -77,6 +77,11 @@ export function destroy(): void { /* deactivate subscriptions */ }
|
|
|
77
77
|
register — it returns the module's root `CategoryInfo`. `AddFrontendModule` is what surfaces the
|
|
78
78
|
module's frontend; without it the module is backend-only.
|
|
79
79
|
|
|
80
|
+
`renderer` names the framework (Vue 3), not a loader release. The `@antelopejs/dms-frontend`
|
|
81
|
+
releases the frontend module runs on go in the frontend module's own `package.json`, as
|
|
82
|
+
`"engines": { "@antelopejs/dms-frontend": ">=0.3.2 <0.4.0" }`: the loader refuses the module
|
|
83
|
+
outside that range. Cap it below the next loader minor while the loader is 0.x.
|
|
84
|
+
|
|
80
85
|
## Navigation & access — module pages are owner-only
|
|
81
86
|
|
|
82
87
|
Three constraints are enforced at registration: a page's `category` must **descend from the
|