Page Menu
Home
Sealhub
Search
Configure Global Search
Log In
Files
F969382
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
11 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/component-arguments/component-argument.ts b/src/component-arguments/component-argument.ts
index d83ced7..a690992 100644
--- a/src/component-arguments/component-argument.ts
+++ b/src/component-arguments/component-argument.ts
@@ -1,91 +1,93 @@
-import { JDDContext } from "../index.js";
+import { JDDContext, Table, TableData } from "../index.js";
import { List } from "./list.js";
export type ExtractStructuredComponentArgumentsParsed<C> = {
[property in keyof C]: ExtractParsed<C[property]>;
};
export type ExtractStructuredComponentArgumentsStorage<C> = {
[property in keyof C]: ExtractStorage<C[property]>;
};
export type ExtractStructuredComponentArgumentsReceived<C> = {
[property in keyof C]: ExtractReceived<C[property]>;
};
export type ExtractParsed<C> = C extends List<infer T>
? T[]
: // eslint-disable-next-line @typescript-eslint/no-explicit-any
C extends ComponentArgument<infer T2, any, any>
? T2
+ : C extends Table<infer TT1, infer TT2>
+ ? TableData<TT1, TT2>
: never;
export type ExtractStorage<C> = C extends ComponentArgument<infer _, infer T>
? T
: never;
export type ExtractReceived<C> = C extends ComponentArgument<
infer _,
infer __,
infer T
>
? T
: never;
export abstract class ComponentArgument<
ParsedType,
StorageType = ParsedType,
ReceivedDataType = StorageType
> {
public example_values: ParsedType[] = [];
public parent_argument: ComponentArgument<unknown> | null = null;
abstract getTypeName(): string;
abstract getEmptyValue(): ParsedType;
abstract countWords(value: ParsedType): number;
abstract getSubArgument(
key: string,
value: ParsedType
): ComponentArgument<unknown> | null;
// Received - what we get on the server side when receiveing the request
// Parsed - the useful form of the value, ready for processing
// Storage - how it's represented when storing in a JSON form
// Formdata - how it's represented when it needs to be sent to the server
abstract receivedToParsed(
context: JDDContext,
value: ReceivedDataType
): Promise<ParsedType | ParsedType[]>;
abstract parsedToStorage(
context: JDDContext,
value: ParsedType
): Promise<StorageType>;
abstract storageToParsed(
context: JDDContext,
value: StorageType
): Promise<ParsedType>;
getExampleValue(_context: JDDContext): Promise<ParsedType> | ParsedType {
return this.example_values[
Math.floor(this.example_values.length * Math.random())
];
}
setExampleValues(values: ParsedType[]): this {
this.example_values = values;
return this;
}
hasParent(type: string) {
let parent = this.parent_argument;
while (parent) {
if (parent.getTypeName() == type) {
return true;
}
parent = parent.parent_argument;
}
return false;
}
}
diff --git a/src/component-arguments/list.test.ts b/src/component-arguments/list.test.ts
index 556f466..284b6fd 100644
--- a/src/component-arguments/list.test.ts
+++ b/src/component-arguments/list.test.ts
@@ -1,15 +1,22 @@
import { ExtractParsed } from "./component-argument.js";
import { Image } from "./image.js";
import { List } from "./list.js";
+import { ShortText } from "./short-text.js";
describe("list", () => {
it("allows for creating a list of images", () => {
new List(new Image());
});
it("properly parses list arguments", () => {
const images_arg = new List(new Image());
const images = [] as ExtractParsed<typeof images_arg>;
images.map((e) => e?.file_manager); // if the types are right, this should typecheck OK
+
+ const strings_arg = new List(new ShortText()).setExampleValues([
+ ["a tag"],
+ ]);
+ const strings = [] as ExtractParsed<typeof strings_arg>;
+ strings.map((e) => e?.padStart(0)); // if the types are right, this should typecheck OK
});
});
diff --git a/src/component-arguments/structured.test.ts b/src/component-arguments/structured.test.ts
new file mode 100644
index 0000000..3c76294
--- /dev/null
+++ b/src/component-arguments/structured.test.ts
@@ -0,0 +1,36 @@
+import { ExtractParsed } from "./component-argument.js";
+import { List } from "./list.js";
+import { ShortText } from "./short-text.js";
+import { Structured } from "./structured.js";
+
+describe("structured argument", () => {
+ it("properly extracts parsed type", () => {
+ const args = new Structured({
+ value: new ShortText().setExampleValues([""]),
+ tags: new List(new ShortText()).setExampleValues([["okazja"]]),
+ });
+
+ const values = {
+ value: "test",
+ tags: ["tag1", "tag2"],
+ } as ExtractParsed<typeof args>;
+ values.tags.map((tag) => tag.padStart(10)); // if the types are OK, this will typecheck OK
+ });
+
+ it("properly extracts parsed type within a list", () => {
+ const arg = new List(
+ new Structured({
+ value: new ShortText().setExampleValues([""]),
+ tags: new List(new ShortText()).setExampleValues([["okazja"]]),
+ })
+ );
+
+ const values = [
+ {
+ value: "test",
+ tags: ["tag1", "tag2"],
+ },
+ ] as ExtractParsed<typeof arg>;
+ values[0].tags.map((tag) => tag.padStart(10)); // if the types are OK, this will typecheck OK
+ });
+});
diff --git a/src/component-arguments/table.test.ts b/src/component-arguments/table.test.ts
new file mode 100644
index 0000000..0ebca4a
--- /dev/null
+++ b/src/component-arguments/table.test.ts
@@ -0,0 +1,34 @@
+import { Table } from "./table.js";
+import { Structured } from "./structured.js";
+import { List } from "./list.js";
+import { ShortText } from "./short-text.js";
+import { ExtractParsed } from "./component-argument.js";
+
+describe("table argument", () => {
+ it("properly parses type value", () => {
+ const table_arg = new Table(
+ new Structured(<const>{
+ value: new ShortText().setExampleValues([""]),
+ tags: new List(new ShortText()),
+ }),
+ new Structured({
+ tags: new List(new ShortText()).setExampleValues([["okazja"]]),
+ })
+ );
+
+ const table_value = {
+ rows: [
+ { type: "row", cells: [] },
+ { type: "header", value: "", tags: [] },
+ ],
+ } as ExtractParsed<typeof table_arg>;
+
+ table_value.rows.map((row) => {
+ if (row.type == "row") {
+ row.cells.map((cell) => {
+ cell.tags.map((tag) => tag); // if the types are written correctly, this will typecheck OK
+ });
+ }
+ });
+ });
+});
diff --git a/src/component-arguments/table.ts b/src/component-arguments/table.ts
index 7e58544..6338f35 100644
--- a/src/component-arguments/table.ts
+++ b/src/component-arguments/table.ts
@@ -1,209 +1,209 @@
import { hasShape, predicates } from "@sealcode/ts-predicates";
import { JDDContext } from "../index.js";
import { ComponentArgument } from "./component-argument.js";
import { ContainerArgument } from "./container-argument.js";
type TableHeader<HeaderType> = { type: "header"; header_content: HeaderType };
type TableRegularRow<CellType> = { type: "row"; cells: CellType[] };
export type TableRow<CellType, HeaderType> =
| TableHeader<HeaderType>
| TableRegularRow<CellType>;
export type TableData<CellType, HeaderType> = {
rows: TableRow<CellType, HeaderType>[];
};
export function isTableHeader(x: unknown): x is TableHeader<unknown> {
return hasShape(
{
type: predicates.const("header"),
header_content: predicates.unknown,
},
x
);
}
export function isTableRegularRow<CellType = unknown>(
x: unknown
): x is TableRegularRow<CellType> {
return hasShape(
{
type: predicates.const("row"),
cells: predicates.array(predicates.unknown),
},
x
);
}
export function isTableData<CellType = unknown, HeaderType = unknown>(
x: unknown
): x is TableData<CellType, HeaderType> {
return hasShape(
{
rows: predicates.array(predicates.object),
},
x
);
}
export class Table<CellType, HeaderType> extends ContainerArgument<
TableData<CellType, HeaderType>
> {
constructor(
- public header_type: ComponentArgument<HeaderType>,
- public cell_type: ComponentArgument<CellType>
+ public header_type: ComponentArgument<HeaderType, unknown, unknown>,
+ public cell_type: ComponentArgument<CellType, unknown, unknown>
) {
super();
cell_type.parent_argument = this;
header_type.parent_argument = this;
}
getSubArgument(key: string, value: TableData<CellType, HeaderType>) {
if (isNaN(parseInt(key))) {
return null;
}
const key_n = parseInt(key);
const row = value.rows[key_n];
if (!row) {
return null;
} else if (row.type == "header") {
return this.header_type;
} else {
return this.cell_type;
}
}
getTypeName() {
return "table";
}
getEmptyValue() {
return {
rows: [
{
type: <const>"header",
header_content: this.header_type.getEmptyValue(),
},
{
type: <const>"row",
cells: [this.cell_type.getEmptyValue()],
},
],
};
}
async getExampleValue(context: JDDContext) {
const rows = Math.round(Math.random() * 5);
const columns = Math.round(Math.random() * 5);
const result: TableData<CellType, HeaderType> = {
rows: [
{
type: "header",
header_content: await this.header_type.getExampleValue(
context
),
},
],
};
for (let i = 0; i < rows; i++) {
const cells: CellType[] = [];
for (let j = 0; j < columns; j++) {
// eslint-disable-next-line no-await-in-loop
cells.push(await this.cell_type.getExampleValue(context));
}
result.rows.push({ type: "row", cells });
}
return result;
}
countWords(value: TableData<CellType, HeaderType>): number {
let result = 0;
for (let i = 0; i < value.rows.length; i++) {
const row = value.rows[i];
if (isTableHeader(row)) {
result += this.header_type.countWords(row.header_content);
} else {
for (let j = 0; j < row.cells.length; j++) {
result += this.cell_type.countWords(row.cells[j]);
}
}
}
return result;
}
async processAllSubarguments(
context: JDDContext,
input: unknown,
processing_function: (
argument: ComponentArgument<unknown>,
value: unknown
) => Promise<unknown>
) {
if (!hasShape({ rows: predicates.array(predicates.object) }, input)) {
return { rows: [] };
} else {
const result: TableData<CellType, HeaderType> = { rows: [] };
const row_promises = input.rows.map(async (row, row_index) => {
let new_row: TableRow<CellType, HeaderType>;
if (hasShape({ header_content: predicates.unknown }, row)) {
let header_content = (await processing_function(
this.header_type,
row.header_content
)) as HeaderType | HeaderType[] | null;
if (Array.isArray(header_content)) {
header_content = header_content[0];
}
if (header_content == null) {
header_content = this.header_type.getEmptyValue();
}
new_row = {
type: "header",
header_content,
};
result.rows[row_index] = new_row;
} else if (
hasShape(
{ cells: predicates.array(predicates.unknown) },
row
)
) {
new_row = {
type: "row",
cells: await Promise.all(
row.cells.map(async (cell) => {
const value = (await processing_function(
this.cell_type,
cell
)) as CellType | CellType[] | null;
if (value === null) {
return this.cell_type.getEmptyValue();
} else if (Array.isArray(value)) {
return value[0];
} else {
return value;
}
})
),
};
result.rows[row_index] = new_row;
}
});
await Promise.all(row_promises);
return result;
}
}
getColumnsCount(value: TableData<CellType, HeaderType>) {
return (
(
(
value.rows.filter((row) => row.type == "row")[0] as
| TableRegularRow<CellType>
| undefined
)?.cells || []
).length || 1
);
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Nov 23, 00:01 (1 d, 4 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
547467
Default Alt Text
(11 KB)
Attached To
Mode
R130 jdd
Attached
Detach File
Event Timeline
Log In to Comment