@sealcode/sealgen 0.8.52 → 0.8.53
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/@types/forms/controls/array.d.ts +1 -1
- package/@types/forms/controls/checkbox-group.d.ts +1 -1
- package/@types/forms/controls/collapsible-field-group.d.ts +1 -1
- package/@types/forms/controls/derived-frame.d.ts +1 -0
- package/@types/forms/controls/dropdown.d.ts +2 -2
- package/@types/forms/controls/form-field-control.d.ts +1 -1
- package/@types/forms/controls/html.d.ts +1 -1
- package/@types/forms/controls/multiple-files.d.ts +1 -1
- package/@types/forms/controls/number.d.ts +1 -1
- package/@types/forms/controls/simple-input.d.ts +1 -1
- package/@types/forms/controls/tickable.d.ts +1 -1
- package/@types/forms/fields/field.d.ts +6 -6
- package/@types/forms/fields/file.d.ts +1 -1
- package/@types/forms/form.d.ts +7 -7
- package/@types/index.d.ts +1 -0
- package/@types/page/list.d.ts +13 -17
- package/@types/page/mountable-with-fields.d.ts +2 -2
- package/@types/page/props-parser.d.ts +1 -1
- package/@types/page/sealious-list.d.ts +18 -0
- package/@types/utils/utils.d.ts +3 -3
- package/lib/add-collection.js +7 -7
- package/lib/add-route.js +7 -7
- package/lib/add-route.test.js +4 -4
- package/lib/build.js +14 -10
- package/lib/components/components.js +5 -1
- package/lib/components/tabs.js +2 -2
- package/lib/components/toggle-modal.js +2 -2
- package/lib/find-scss-includes.js +3 -3
- package/lib/forms/controls/array.js +5 -5
- package/lib/forms/controls/big-radio-group.js +2 -2
- package/lib/forms/controls/checkboxed-list-input.js +3 -3
- package/lib/forms/controls/collapsible-field-group.js +1 -1
- package/lib/forms/controls/controls.js +5 -1
- package/lib/forms/controls/derived-frame.js +3 -3
- package/lib/forms/controls/derived.js +2 -2
- package/lib/forms/controls/dropdown.js +2 -2
- package/lib/forms/controls/editable-collection-subset.js +3 -3
- package/lib/forms/controls/field-group.js +1 -1
- package/lib/forms/controls/file.js +1 -1
- package/lib/forms/controls/hidden-input.js +1 -1
- package/lib/forms/controls/multiple-files.js +5 -5
- package/lib/forms/controls/multiple-photos.js +1 -1
- package/lib/forms/controls/photo.js +1 -1
- package/lib/forms/controls/simple-input.js +2 -2
- package/lib/forms/controls/tickable.js +3 -3
- package/lib/forms/fields/boolean.js +3 -3
- package/lib/forms/fields/checkboxed-list.js +3 -3
- package/lib/forms/fields/enum-multiple.js +3 -3
- package/lib/forms/fields/enum.js +2 -2
- package/lib/forms/fields/field.js +6 -2
- package/lib/forms/fields/file.js +6 -6
- package/lib/forms/fields/number.js +5 -5
- package/lib/forms/fields/pick-from-list.js +1 -1
- package/lib/forms/fields/regexp.js +2 -2
- package/lib/forms/fields/simple-form-field.js +4 -4
- package/lib/forms/form.js +9 -9
- package/lib/forms/form.test.js +12 -12
- package/lib/forms/multiform.js +5 -5
- package/lib/forms/multiform.test.js +26 -26
- package/lib/generate-collections.js +8 -4
- package/lib/generate-routes.js +12 -8
- package/lib/index.js +6 -1
- package/lib/make-env.js +1 -1
- package/lib/make-hidden-inputs.js +3 -3
- package/lib/page/list.js +66 -53
- package/lib/page/mountable-with-fields.js +2 -2
- package/lib/page/page.js +1 -1
- package/lib/page/sealious-list.js +53 -0
- package/lib/page/stateful-page.js +9 -9
- package/lib/templates/form.js +1 -1
- package/lib/templates/list.js +9 -8
- package/lib/templates/multiform.js +1 -1
- package/lib/templates/page.js +1 -1
- package/lib/templates/route-test.js +1 -1
- package/lib/templates/stateful-page.js +1 -1
- package/lib/test_utils/locator_is_visible.js +1 -1
- package/lib/utils/exec.js +1 -1
- package/lib/utils/import-path.js +2 -2
- package/lib/utils/input-wrapper.js +1 -1
- package/lib/utils/list-collections.js +2 -2
- package/lib/utils/prettier.js +6 -2
- package/lib/utils/question.js +1 -1
- package/lib/utils/render-attributes.js +1 -1
- package/lib/utils/utils.js +5 -1
- package/lib/utils/walk.js +1 -1
- package/package.json +2 -2
- package/src/index.ts +1 -0
- package/src/page/list.ts +70 -67
- package/src/page/sealious-list.ts +76 -0
- package/src/templates/list.ts +7 -6
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SealiousItemListPage = void 0;
|
|
4
|
+
const list_1 = require("./list");
|
|
5
|
+
const access_control_1 = require("./access-control");
|
|
6
|
+
class SealiousItemListPage extends list_1.ListPage {
|
|
7
|
+
constructor(collection) {
|
|
8
|
+
super();
|
|
9
|
+
this.collection = collection;
|
|
10
|
+
this.canAccess = (0, access_control_1.peopleWhoCan)("list", this.collection);
|
|
11
|
+
}
|
|
12
|
+
async getContextForList(ctx) {
|
|
13
|
+
return ctx.$context;
|
|
14
|
+
}
|
|
15
|
+
async getTotalPages(ctx, itemsPerPage) {
|
|
16
|
+
const query = this.collection.list(await this.getContextForList(ctx));
|
|
17
|
+
await this.addFilter(ctx, query);
|
|
18
|
+
const { items } = await query.fetch();
|
|
19
|
+
return Math.ceil(items.length / itemsPerPage);
|
|
20
|
+
}
|
|
21
|
+
async addFilter(ctx, list) {
|
|
22
|
+
const filters = await this.getFilterValues(ctx);
|
|
23
|
+
for (const key in filters) {
|
|
24
|
+
if (filters[key] === undefined || filters[key] == "") {
|
|
25
|
+
delete filters[key];
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
if (Object.keys(filters).length) {
|
|
29
|
+
list.filter(filters);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
async makeQuery(ctx) {
|
|
33
|
+
const query = this.collection.list(await this.getContextForList(ctx));
|
|
34
|
+
const sort = await this.getSort(ctx);
|
|
35
|
+
if (sort) {
|
|
36
|
+
query.sort({ [sort.field]: sort.order });
|
|
37
|
+
}
|
|
38
|
+
return query;
|
|
39
|
+
}
|
|
40
|
+
async getItems(ctx, page, itemsPerPage) {
|
|
41
|
+
const query = await this.makeQuery(ctx);
|
|
42
|
+
await this.addFilter(ctx, query);
|
|
43
|
+
const { items } = await query
|
|
44
|
+
.paginate({ items: itemsPerPage, page })
|
|
45
|
+
.fetch();
|
|
46
|
+
return items;
|
|
47
|
+
}
|
|
48
|
+
async renderItem(_, item, index) {
|
|
49
|
+
return `<div>${item.id}</div>`;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
exports.SealiousItemListPage = SealiousItemListPage;
|
|
53
|
+
//# sourceMappingURL=sealious-list.js.map
|
|
@@ -21,18 +21,18 @@ class StatefulPage extends mountable_1.Mountable {
|
|
|
21
21
|
};
|
|
22
22
|
}
|
|
23
23
|
wrapInForm(state, content) {
|
|
24
|
-
return tempstream_1.tempstream /* HTML */ `<form action="./" method="POST">
|
|
24
|
+
return (0, tempstream_1.tempstream /* HTML */) `<form action="./" method="POST">
|
|
25
25
|
<input
|
|
26
26
|
name="state"
|
|
27
27
|
type="hidden"
|
|
28
|
-
value="${base64_1.to_base64(JSON.stringify(state))}"
|
|
28
|
+
value="${(0, base64_1.to_base64)(JSON.stringify(state))}"
|
|
29
29
|
/>
|
|
30
30
|
${content}
|
|
31
31
|
</form>`;
|
|
32
32
|
}
|
|
33
33
|
makeActionButton(state, action_description, ...args) {
|
|
34
34
|
let label, action;
|
|
35
|
-
if (ts_predicates_1.is(action_description, ts_predicates_1.predicates.object)) {
|
|
35
|
+
if ((0, ts_predicates_1.is)(action_description, ts_predicates_1.predicates.object)) {
|
|
36
36
|
action = action_description.action.toString();
|
|
37
37
|
label = action_description.label || action;
|
|
38
38
|
}
|
|
@@ -44,12 +44,12 @@ class StatefulPage extends mountable_1.Mountable {
|
|
|
44
44
|
<input
|
|
45
45
|
type="submit"
|
|
46
46
|
value=${label}
|
|
47
|
-
formaction="./?action=${action}&args=${base64_1.to_base64(JSON.stringify(args))}"
|
|
47
|
+
formaction="./?action=${action}&args=${(0, base64_1.to_base64)(JSON.stringify(args))}"
|
|
48
48
|
/>
|
|
49
49
|
`;
|
|
50
50
|
}
|
|
51
51
|
extractState(ctx) {
|
|
52
|
-
if (!ts_predicates_1.hasShape({
|
|
52
|
+
if (!(0, ts_predicates_1.hasShape)({
|
|
53
53
|
action: ts_predicates_1.predicates.maybe(ts_predicates_1.predicates.string),
|
|
54
54
|
state: ts_predicates_1.predicates.string,
|
|
55
55
|
args: ts_predicates_1.predicates.maybe(ts_predicates_1.predicates.string),
|
|
@@ -60,8 +60,8 @@ class StatefulPage extends mountable_1.Mountable {
|
|
|
60
60
|
}
|
|
61
61
|
const inputs = Object.fromEntries(Object.entries(ctx.$body).filter(([key]) => !["action", "state", "args", "$"].includes(key)));
|
|
62
62
|
// the "$" key is parsed as dot notation and overrides the state
|
|
63
|
-
const original_state = JSON.parse(base64_1.from_base64(ctx.$body.state));
|
|
64
|
-
const modified_state = deepmerge_1.default(original_state, ctx.$body.$ || {}, {
|
|
63
|
+
const original_state = JSON.parse((0, base64_1.from_base64)(ctx.$body.state));
|
|
64
|
+
const modified_state = (0, deepmerge_1.default)(original_state, ctx.$body.$ || {}, {
|
|
65
65
|
arrayMerge: (target, source, options) => {
|
|
66
66
|
// https://github.com/TehShrike/deepmerge#user-content-arraymerge-example-combine-arrays
|
|
67
67
|
const destination = target.slice();
|
|
@@ -71,7 +71,7 @@ class StatefulPage extends mountable_1.Mountable {
|
|
|
71
71
|
options.cloneUnlessOtherwiseSpecified(item, options);
|
|
72
72
|
}
|
|
73
73
|
else if (options.isMergeableObject(item)) {
|
|
74
|
-
destination[index] = deepmerge_1.default(target[index], item, options);
|
|
74
|
+
destination[index] = (0, deepmerge_1.default)(target[index], item, options);
|
|
75
75
|
}
|
|
76
76
|
else if (target.indexOf(item) === -1) {
|
|
77
77
|
destination.push(item);
|
|
@@ -104,7 +104,7 @@ class StatefulPage extends mountable_1.Mountable {
|
|
|
104
104
|
router.post(path, (ctx) => {
|
|
105
105
|
const { action, state, inputs, action_args } = this.extractState(ctx);
|
|
106
106
|
if (action) {
|
|
107
|
-
ctx.body = this.render(ctx, this.actions[action](state, inputs, ...JSON.parse(base64_1.from_base64(action_args))), inputs);
|
|
107
|
+
ctx.body = this.render(ctx, this.actions[action](state, inputs, ...JSON.parse((0, base64_1.from_base64)(action_args))), inputs);
|
|
108
108
|
}
|
|
109
109
|
else {
|
|
110
110
|
ctx.body = this.render(ctx, state, inputs);
|
package/lib/templates/form.js
CHANGED
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.formTemplate = void 0;
|
|
4
4
|
const import_path_1 = require("../utils/import-path");
|
|
5
5
|
async function formTemplate(action_name, newfilefullpath) {
|
|
6
|
-
const rel = import_path_1.curryImportPath(newfilefullpath);
|
|
6
|
+
const rel = (0, import_path_1.curryImportPath)(newfilefullpath);
|
|
7
7
|
return `import { Context } from "koa";
|
|
8
8
|
import { Form, FormData, FormDataValue, Fields, Controls } from "@sealcode/sealgen";
|
|
9
9
|
import html from "${rel("src/back/html")}";
|
package/lib/templates/list.js
CHANGED
|
@@ -9,14 +9,14 @@ const listTemplate = async (action_name, newfilefullpath) => {
|
|
|
9
9
|
type: "autocomplete",
|
|
10
10
|
name: "collection",
|
|
11
11
|
message: "Which sealious collection do you want to list?",
|
|
12
|
-
choices: (await list_collections_1.listCollections()).map((collection) => ({
|
|
12
|
+
choices: (await (0, list_collections_1.listCollections)()).map((collection) => ({
|
|
13
13
|
title: collection,
|
|
14
14
|
value: collection,
|
|
15
15
|
})),
|
|
16
16
|
});
|
|
17
17
|
const collection = response.collection;
|
|
18
18
|
const uppercase_collection = collection[0].toLocaleUpperCase() + collection.slice(1);
|
|
19
|
-
const rel = import_path_1.curryImportPath(newfilefullpath);
|
|
19
|
+
const rel = (0, import_path_1.curryImportPath)(newfilefullpath);
|
|
20
20
|
return `import { Context } from "koa";
|
|
21
21
|
import { CollectionItem } from "sealious";
|
|
22
22
|
import { tempstream } from "tempstream";
|
|
@@ -44,12 +44,13 @@ export default new (class ${action_name}Page extends SealiousItemListPage<
|
|
|
44
44
|
tempstream/* HTML */ \`<div>
|
|
45
45
|
<h2>${uppercase_collection} List</h2>
|
|
46
46
|
<table>
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
47
|
+
\${this.renderTableHead(ctx, [
|
|
48
|
+
{ field: "id" },
|
|
49
|
+
...Object.keys(Patrons.fields).map((field) => ({
|
|
50
|
+
field,
|
|
51
|
+
label: field,
|
|
52
|
+
})),
|
|
53
|
+
])}
|
|
53
54
|
<tbody>
|
|
54
55
|
\${super.render(ctx)}
|
|
55
56
|
</tbody>
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.multiformTemplate = void 0;
|
|
4
4
|
const utils_1 = require("../utils/utils");
|
|
5
5
|
async function multiformTemplate(action_name, newfilefullpath) {
|
|
6
|
-
const rel = utils_1.curryImportPath(newfilefullpath);
|
|
6
|
+
const rel = (0, utils_1.curryImportPath)(newfilefullpath);
|
|
7
7
|
return `import { Context } from "koa";
|
|
8
8
|
import html from "${rel("src/back/html")}";
|
|
9
9
|
import { Controls, Fields, Form, FormMessage, Multiform } from "@sealcode/sealgen";
|
package/lib/templates/page.js
CHANGED
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.pageTemplate = void 0;
|
|
4
4
|
const import_path_1 = require("../utils/import-path");
|
|
5
5
|
async function pageTemplate(action_name, newfilefullpath) {
|
|
6
|
-
const rel = import_path_1.curryImportPath(newfilefullpath);
|
|
6
|
+
const rel = (0, import_path_1.curryImportPath)(newfilefullpath);
|
|
7
7
|
return `import { Context } from "koa";
|
|
8
8
|
import { tempstream } from "tempstream";
|
|
9
9
|
import { Page } from "@sealcode/sealgen";
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.testTemplate = void 0;
|
|
4
4
|
const import_path_1 = require("../utils/import-path");
|
|
5
5
|
function testTemplate(action_name, new_file_fullpath) {
|
|
6
|
-
const rel = import_path_1.curryImportPath(new_file_fullpath);
|
|
6
|
+
const rel = (0, import_path_1.curryImportPath)(new_file_fullpath);
|
|
7
7
|
return `import { withProdApp } from "${rel("src/back/test_utils/with-prod-app")}";
|
|
8
8
|
import { LONG_TEST_TIMEOUT, webhintURL } from "${rel("src/back/test_utils/webhint")}";
|
|
9
9
|
import { ${action_name}URL } from "${rel("src/back/routes/urls")}";
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.statefulPageTemplate = void 0;
|
|
4
4
|
const import_path_1 = require("../utils/import-path");
|
|
5
5
|
async function statefulPageTemplate(action_name, newfilefullpath) {
|
|
6
|
-
const rel = import_path_1.curryImportPath(newfilefullpath);
|
|
6
|
+
const rel = (0, import_path_1.curryImportPath)(newfilefullpath);
|
|
7
7
|
return `import { BaseContext } from "koa";
|
|
8
8
|
import { tempstream, Templatable } from "tempstream";
|
|
9
9
|
import { StatefulPage } from "@sealcode/sealgen";
|
|
@@ -5,7 +5,7 @@ const sleep_1 = require("../utils/sleep");
|
|
|
5
5
|
async function locator_is_visible(locator, timeout = 500) {
|
|
6
6
|
let exists = false;
|
|
7
7
|
while (timeout > 0 && !exists) {
|
|
8
|
-
await sleep_1.sleep(50);
|
|
8
|
+
await (0, sleep_1.sleep)(50);
|
|
9
9
|
timeout -= 50;
|
|
10
10
|
exists = await locator.isVisible();
|
|
11
11
|
}
|
package/lib/utils/exec.js
CHANGED
|
@@ -7,7 +7,7 @@ async function exec(program, args) {
|
|
|
7
7
|
return new Promise((resolve, reject) => {
|
|
8
8
|
let stdout = "";
|
|
9
9
|
let stderr = "";
|
|
10
|
-
const process = child_process_1.spawn(program, args);
|
|
10
|
+
const process = (0, child_process_1.spawn)(program, args);
|
|
11
11
|
process.stdout.on("data", (data) => (stdout += data.toString()));
|
|
12
12
|
process.stderr.on("data", (data) => (stderr += data.toString()));
|
|
13
13
|
process.on("close", (exitCode) => {
|
package/lib/utils/import-path.js
CHANGED
|
@@ -6,9 +6,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.curryImportPath = exports.importPath = void 0;
|
|
7
7
|
const path_1 = require("path");
|
|
8
8
|
const locreq_1 = __importDefault(require("locreq"));
|
|
9
|
-
const target_locreq = locreq_1.default(process.cwd());
|
|
9
|
+
const target_locreq = (0, locreq_1.default)(process.cwd());
|
|
10
10
|
function importPath(project_module_path, project_target_path) {
|
|
11
|
-
let result = path_1.relative(path_1.dirname(target_locreq.resolve(project_module_path)), target_locreq.resolve(project_target_path).replace(/.ts$/, ""));
|
|
11
|
+
let result = (0, path_1.relative)((0, path_1.dirname)(target_locreq.resolve(project_module_path)), target_locreq.resolve(project_target_path).replace(/.ts$/, ""));
|
|
12
12
|
if (result[0] != ".") {
|
|
13
13
|
result = "./" + result;
|
|
14
14
|
}
|
|
@@ -10,7 +10,7 @@ function getModifierClasses(modifiers) {
|
|
|
10
10
|
return modifiers.map((m) => "form-input__wrapper--" + m).join(" ");
|
|
11
11
|
}
|
|
12
12
|
function inputWrapper(modifiers, input) {
|
|
13
|
-
return tempstream_1.tempstream /* HTML */ `<div
|
|
13
|
+
return (0, tempstream_1.tempstream /* HTML */) `<div
|
|
14
14
|
class="form-input form-input__wrapper ${getModifierClasses(modifiers)}"
|
|
15
15
|
>
|
|
16
16
|
${input}
|
|
@@ -6,9 +6,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.listCollections = void 0;
|
|
7
7
|
const locreq_1 = __importDefault(require("locreq"));
|
|
8
8
|
const walk_1 = require("./walk");
|
|
9
|
-
const target_locreq = locreq_1.default(process.cwd());
|
|
9
|
+
const target_locreq = (0, locreq_1.default)(process.cwd());
|
|
10
10
|
async function listCollections() {
|
|
11
|
-
const files = await walk_1.walkDir(target_locreq.resolve("src/back/collections"));
|
|
11
|
+
const files = await (0, walk_1.walkDir)(target_locreq.resolve("src/back/collections"));
|
|
12
12
|
return files
|
|
13
13
|
.filter((f) => f.endsWith(".ts") &&
|
|
14
14
|
!f.endsWith(".test.ts") &&
|
package/lib/utils/prettier.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
3
|
if (k2 === undefined) k2 = k;
|
|
4
|
-
Object.
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
5
9
|
}) : (function(o, m, k, k2) {
|
|
6
10
|
if (k2 === undefined) k2 = k;
|
|
7
11
|
o[k2] = m[k];
|
|
@@ -25,7 +29,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
25
29
|
exports.getPrettierConfig = void 0;
|
|
26
30
|
const prettier = __importStar(require("prettier"));
|
|
27
31
|
const locreq_1 = __importDefault(require("locreq"));
|
|
28
|
-
const target_locreq = locreq_1.default(process.cwd());
|
|
32
|
+
const target_locreq = (0, locreq_1.default)(process.cwd());
|
|
29
33
|
async function getPrettierConfig() {
|
|
30
34
|
return {
|
|
31
35
|
...(await prettier.resolveConfig(target_locreq.resolve("src/back/routes"))),
|
package/lib/utils/question.js
CHANGED
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.question = void 0;
|
|
4
4
|
const readline_1 = require("readline");
|
|
5
5
|
async function question(text, validator) {
|
|
6
|
-
const rl = readline_1.createInterface({
|
|
6
|
+
const rl = (0, readline_1.createInterface)({
|
|
7
7
|
input: process.stdin,
|
|
8
8
|
output: process.stdout,
|
|
9
9
|
});
|
package/lib/utils/utils.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
3
|
if (k2 === undefined) k2 = k;
|
|
4
|
-
Object.
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
5
9
|
}) : (function(o, m, k, k2) {
|
|
6
10
|
if (k2 === undefined) k2 = k;
|
|
7
11
|
o[k2] = m[k];
|
package/lib/utils/walk.js
CHANGED
|
@@ -10,7 +10,7 @@ async function walkDir(dir) {
|
|
|
10
10
|
if (entry.includes(".#")) {
|
|
11
11
|
continue;
|
|
12
12
|
}
|
|
13
|
-
const fullpath = path_1.resolve(dir, entry);
|
|
13
|
+
const fullpath = (0, path_1.resolve)(dir, entry);
|
|
14
14
|
// eslint-disable-next-line no-await-in-loop
|
|
15
15
|
const info = await fs_1.promises.stat(fullpath);
|
|
16
16
|
if (info.isDirectory()) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sealcode/sealgen",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.53",
|
|
4
4
|
"description": "Module to automate adding routes and collections to a sealious application",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"playwright": "^1.29.2",
|
|
41
41
|
"source-map-support": "^0.5.21",
|
|
42
42
|
"ts-node": "^10.9.1",
|
|
43
|
-
"typescript": "^4.
|
|
43
|
+
"typescript": "^4.4",
|
|
44
44
|
"wait-port": "^1.0.4"
|
|
45
45
|
},
|
|
46
46
|
"types": "./@types/index.d.ts",
|
package/src/index.ts
CHANGED
|
@@ -18,6 +18,7 @@ export * from "./forms/form";
|
|
|
18
18
|
export * from "./page/props-parser";
|
|
19
19
|
export * from "./utils/utils";
|
|
20
20
|
export * from "./page/list";
|
|
21
|
+
export * from "./page/sealious-list";
|
|
21
22
|
export * from "./utils/translate";
|
|
22
23
|
export * as sanitize from "./sanitize";
|
|
23
24
|
export * from "./forms/multiform";
|
package/src/page/list.ts
CHANGED
|
@@ -1,21 +1,13 @@
|
|
|
1
1
|
import { Context } from "koa";
|
|
2
|
-
import {
|
|
3
|
-
Collection,
|
|
4
|
-
CollectionItem,
|
|
5
|
-
ItemList,
|
|
6
|
-
Context as SealiousContext,
|
|
7
|
-
CollectionInput,
|
|
8
|
-
} from "sealious";
|
|
9
|
-
|
|
10
2
|
import { Templatable, tempstream, FlatTemplatable } from "tempstream";
|
|
11
|
-
import { peopleWhoCan } from "./access-control";
|
|
12
3
|
import { Page } from "./page";
|
|
13
4
|
import { ShapeToType } from "@sealcode/ts-predicates";
|
|
14
5
|
import { FormControl } from "../forms/controls/controls";
|
|
15
|
-
import { FormField, NumberField } from "../forms/fields/field";
|
|
6
|
+
import { FormField, NumberField, SimpleFormField } from "../forms/fields/field";
|
|
16
7
|
import { naturalNumbers, UrlWithNewParams } from "../utils/utils";
|
|
17
8
|
import { FormDataValue } from "..";
|
|
18
9
|
import { makeHiddenInputs } from "../make-hidden-inputs";
|
|
10
|
+
import qs from "qs";
|
|
19
11
|
|
|
20
12
|
export const BasePagePropsShape = <const>{};
|
|
21
13
|
export type BasePageProps = ShapeToType<typeof BasePagePropsShape>;
|
|
@@ -23,8 +15,33 @@ export type BasePageProps = ShapeToType<typeof BasePagePropsShape>;
|
|
|
23
15
|
export const BaseListPageFields = <const>{
|
|
24
16
|
page: new NumberField(false, 1),
|
|
25
17
|
itemsPerPage: new NumberField(false, 12),
|
|
18
|
+
sort: new SimpleFormField(false),
|
|
26
19
|
};
|
|
27
20
|
|
|
21
|
+
const SORT_SEPARATOR = ":";
|
|
22
|
+
function decodeSort(
|
|
23
|
+
s: unknown
|
|
24
|
+
): { field: string; order: "asc" | "desc" } | null {
|
|
25
|
+
if (typeof s !== "string" || !s.includes(SORT_SEPARATOR)) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
let order = s.split(SORT_SEPARATOR)[1];
|
|
29
|
+
const field = s.split(SORT_SEPARATOR)[0];
|
|
30
|
+
if (order !== "asc" && order !== "desc") {
|
|
31
|
+
order = "asc";
|
|
32
|
+
return {
|
|
33
|
+
field,
|
|
34
|
+
order: order as "asc",
|
|
35
|
+
};
|
|
36
|
+
} else {
|
|
37
|
+
return { field, order };
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function encodeSort(field: string, order: "asc" | "desc") {
|
|
42
|
+
return [field, order].join(SORT_SEPARATOR);
|
|
43
|
+
}
|
|
44
|
+
|
|
28
45
|
export abstract class ListPage<
|
|
29
46
|
ItemType,
|
|
30
47
|
F extends typeof BaseListPageFields
|
|
@@ -158,6 +175,21 @@ ${this.renderListContainer(ctx, this.renderItems(ctx, values))}`;
|
|
|
158
175
|
return filter;
|
|
159
176
|
}
|
|
160
177
|
|
|
178
|
+
async getSort(
|
|
179
|
+
ctx: Context
|
|
180
|
+
): Promise<{ field: string; order: "asc" | "desc" } | null> {
|
|
181
|
+
const { sort } = await this.extractRawValues(ctx);
|
|
182
|
+
return decodeSort(sort);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
makeSortLink(ctx: Context, field: string, order: "asc" | "desc"): string {
|
|
186
|
+
const url = new URL(ctx.url, "https://example.com");
|
|
187
|
+
const params = qs.parse(url.search.slice(1));
|
|
188
|
+
params.sort = encodeSort(field, order);
|
|
189
|
+
url.search = qs.stringify(params);
|
|
190
|
+
return url.pathname + url.search;
|
|
191
|
+
}
|
|
192
|
+
|
|
161
193
|
async renderFilters(ctx: Context): Promise<FlatTemplatable> {
|
|
162
194
|
const values = await this.extractRawValues(ctx);
|
|
163
195
|
return tempstream/* HTML */ `<form method="GET">
|
|
@@ -177,64 +209,35 @@ ${this.renderListContainer(ctx, this.renderItems(ctx, values))}`;
|
|
|
177
209
|
})}
|
|
178
210
|
</form>`;
|
|
179
211
|
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
export abstract class SealiousItemListPage<
|
|
183
|
-
C extends Collection,
|
|
184
|
-
F extends typeof BaseListPageFields
|
|
185
|
-
> extends ListPage<CollectionItem<C>, F> {
|
|
186
|
-
constructor(public collection: C) {
|
|
187
|
-
super();
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
async getContextForList(ctx: Context): Promise<SealiousContext> {
|
|
191
|
-
return ctx.$context;
|
|
192
|
-
}
|
|
193
212
|
|
|
194
|
-
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
async makeQuery(ctx: Context): Promise<ItemList<C>> {
|
|
215
|
-
return this.collection.list(await this.getContextForList(ctx));
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
async getItems(
|
|
213
|
+
renderHeading(ctx: Context, field: string, label = field): FlatTemplatable {
|
|
214
|
+
const current_sort = decodeSort(
|
|
215
|
+
qs.parse(
|
|
216
|
+
new URL(ctx.url, "https://www.example.com").search.slice(1)
|
|
217
|
+
)["sort"]
|
|
218
|
+
);
|
|
219
|
+
const current_order =
|
|
220
|
+
current_sort?.field == field ? current_sort.order : null;
|
|
221
|
+
const order = current_order == "desc" ? "asc" : "desc";
|
|
222
|
+
return /* HTML */ `<th>
|
|
223
|
+
<a href="${this.makeSortLink(ctx, field, order)}"
|
|
224
|
+
>${label}
|
|
225
|
+
${(current_order && (current_order == "asc" ? "↑" : "↓")) ||
|
|
226
|
+
""}</a
|
|
227
|
+
>
|
|
228
|
+
</th>`;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
renderTableHead(
|
|
219
232
|
ctx: Context,
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
async renderItem(
|
|
232
|
-
_: Context,
|
|
233
|
-
item: CollectionItem<C>,
|
|
234
|
-
index: number
|
|
235
|
-
): Promise<FlatTemplatable> {
|
|
236
|
-
return `<div>${item.id}</div>`;
|
|
233
|
+
fields: { field: string; label?: string }[]
|
|
234
|
+
): FlatTemplatable {
|
|
235
|
+
return tempstream/* HTML */ `<thead>
|
|
236
|
+
<tr>
|
|
237
|
+
${fields.map(({ label, field }) =>
|
|
238
|
+
this.renderHeading(ctx, field, label)
|
|
239
|
+
)}
|
|
240
|
+
</tr>
|
|
241
|
+
</thead>`;
|
|
237
242
|
}
|
|
238
|
-
|
|
239
|
-
canAccess = peopleWhoCan("list", this.collection);
|
|
240
243
|
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { Context } from "koa";
|
|
2
|
+
import { BaseListPageFields, ListPage } from "./list";
|
|
3
|
+
import {
|
|
4
|
+
Collection,
|
|
5
|
+
CollectionItem,
|
|
6
|
+
ItemList,
|
|
7
|
+
Context as SealiousContext,
|
|
8
|
+
CollectionInput,
|
|
9
|
+
} from "sealious";
|
|
10
|
+
import { FlatTemplatable } from "tempstream";
|
|
11
|
+
import { peopleWhoCan } from "./access-control";
|
|
12
|
+
|
|
13
|
+
export abstract class SealiousItemListPage<
|
|
14
|
+
C extends Collection,
|
|
15
|
+
F extends typeof BaseListPageFields
|
|
16
|
+
> extends ListPage<CollectionItem<C>, F> {
|
|
17
|
+
constructor(public collection: C) {
|
|
18
|
+
super();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async getContextForList(ctx: Context): Promise<SealiousContext> {
|
|
22
|
+
return ctx.$context;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async getTotalPages(ctx: Context, itemsPerPage: number): Promise<number> {
|
|
26
|
+
const query = this.collection.list(await this.getContextForList(ctx));
|
|
27
|
+
await this.addFilter(ctx, query);
|
|
28
|
+
const { items } = await query.fetch();
|
|
29
|
+
return Math.ceil(items.length / itemsPerPage);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
protected async addFilter(ctx: Context, list: ItemList<C>): Promise<void> {
|
|
33
|
+
const filters = await this.getFilterValues(ctx);
|
|
34
|
+
|
|
35
|
+
for (const key in filters) {
|
|
36
|
+
if (filters[key] === undefined || filters[key] == "") {
|
|
37
|
+
delete filters[key];
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (Object.keys(filters).length) {
|
|
41
|
+
list.filter(filters as Partial<CollectionInput<C>>);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async makeQuery(ctx: Context): Promise<ItemList<C>> {
|
|
46
|
+
const query = this.collection.list(await this.getContextForList(ctx));
|
|
47
|
+
const sort = await this.getSort(ctx);
|
|
48
|
+
if (sort) {
|
|
49
|
+
query.sort({ [sort.field]: sort.order } as any);
|
|
50
|
+
}
|
|
51
|
+
return query;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async getItems(
|
|
55
|
+
ctx: Context,
|
|
56
|
+
page: number,
|
|
57
|
+
itemsPerPage: number
|
|
58
|
+
): Promise<CollectionItem<C>[]> {
|
|
59
|
+
const query = await this.makeQuery(ctx);
|
|
60
|
+
await this.addFilter(ctx, query);
|
|
61
|
+
const { items } = await query
|
|
62
|
+
.paginate({ items: itemsPerPage, page })
|
|
63
|
+
.fetch();
|
|
64
|
+
return items;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async renderItem(
|
|
68
|
+
_: Context,
|
|
69
|
+
item: CollectionItem<C>,
|
|
70
|
+
index: number
|
|
71
|
+
): Promise<FlatTemplatable> {
|
|
72
|
+
return `<div>${item.id}</div>`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
canAccess = peopleWhoCan("list", this.collection);
|
|
76
|
+
}
|
package/src/templates/list.ts
CHANGED
|
@@ -50,12 +50,13 @@ export default new (class ${action_name}Page extends SealiousItemListPage<
|
|
|
50
50
|
tempstream/* HTML */ \`<div>
|
|
51
51
|
<h2>${uppercase_collection} List</h2>
|
|
52
52
|
<table>
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
53
|
+
\${this.renderTableHead(ctx, [
|
|
54
|
+
{ field: "id" },
|
|
55
|
+
...Object.keys(Patrons.fields).map((field) => ({
|
|
56
|
+
field,
|
|
57
|
+
label: field,
|
|
58
|
+
})),
|
|
59
|
+
])}
|
|
59
60
|
<tbody>
|
|
60
61
|
\${super.render(ctx)}
|
|
61
62
|
</tbody>
|