tsondb 0.8.4 → 0.9.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/dist/src/shared/utils/markdown.js +2 -2
- package/dist/src/web/components/InstanceRouteSkeleton.d.ts +30 -0
- package/dist/src/web/components/InstanceRouteSkeleton.js +52 -1
- package/dist/src/web/routes/CreateInstance.js +32 -26
- package/dist/src/web/routes/Instance.js +10 -4
- package/dist/src/web/utils/BlockMarkdown.d.ts +1 -0
- package/dist/src/web/utils/BlockMarkdown.js +8 -5
- package/dist/src/web/utils/InlineMarkdown.js +4 -2
- package/dist/src/web/utils/Markdown.d.ts +1 -0
- package/dist/src/web/utils/Markdown.js +2 -2
- package/package.json +1 -1
|
@@ -24,7 +24,7 @@ const boldWithItalicRule = {
|
|
|
24
24
|
}),
|
|
25
25
|
};
|
|
26
26
|
const italicWithBoldRule = {
|
|
27
|
-
pattern: /(?<![\\*]|[
|
|
27
|
+
pattern: /(?<![\\*]|[^\\]\*.*)\*(?=\*\*|[^*])([^*]*?\*\*[^*]*?\*\*[^*]*?)(?<=[^\\*]|[^\\]\*\*)\*(?!\*)/,
|
|
28
28
|
map: (result, parseInside) => ({
|
|
29
29
|
kind: "italic",
|
|
30
30
|
content: parseInside(result[1] ?? ""),
|
|
@@ -494,7 +494,7 @@ const addIndentationToSyntax = (nodes, nextUpperNode) => nodes.reduce((accNodes,
|
|
|
494
494
|
}
|
|
495
495
|
}, []);
|
|
496
496
|
const footnoteRule = {
|
|
497
|
-
pattern: /^\[\^([a-zA-Z0-9
|
|
497
|
+
pattern: /^\[\^([a-zA-Z0-9]+?)\]: (.+?(?:\n(?: {2}.+)?)*)(\n{2,}|\s*$)/,
|
|
498
498
|
map: ([_match, label = "", content = "", _trailingWhitespace]) => ({
|
|
499
499
|
kind: "footnote",
|
|
500
500
|
label: label,
|
|
@@ -12,6 +12,21 @@ export type InstanceRouteSkeletonInitializer = (values: {
|
|
|
12
12
|
setInstanceContent: Dispatch<SetStateAction<unknown>>;
|
|
13
13
|
getDeclFromDeclName: GetDeclFromDeclName;
|
|
14
14
|
}) => Promise<void>;
|
|
15
|
+
export type InstanceRouteSkeletonSubmitHandler<A extends string = string> = (values: {
|
|
16
|
+
locales: string[];
|
|
17
|
+
entity: SerializedEntityDecl;
|
|
18
|
+
instanceId: string | undefined;
|
|
19
|
+
instanceContent: unknown;
|
|
20
|
+
action: A;
|
|
21
|
+
customId: string;
|
|
22
|
+
isLocaleEntity: boolean | undefined;
|
|
23
|
+
childInstances: UnsafeEntityTaggedInstanceContainerWithChildInstances[];
|
|
24
|
+
route: LocationHook["route"];
|
|
25
|
+
setInstanceContent: Dispatch<SetStateAction<unknown>>;
|
|
26
|
+
setCustomId: Dispatch<SetStateAction<string>>;
|
|
27
|
+
getDeclFromDeclName: GetDeclFromDeclName;
|
|
28
|
+
updateLocalGitState?: () => Promise<void>;
|
|
29
|
+
}) => Promise<void>;
|
|
15
30
|
export type InstanceRouteSkeletonOnSubmitHandler = (values: {
|
|
16
31
|
locales: string[];
|
|
17
32
|
entity: SerializedEntityDecl;
|
|
@@ -27,6 +42,20 @@ export type InstanceRouteSkeletonOnSubmitHandler = (values: {
|
|
|
27
42
|
getDeclFromDeclName: GetDeclFromDeclName;
|
|
28
43
|
updateLocalGitState?: () => Promise<void>;
|
|
29
44
|
}) => Promise<void>;
|
|
45
|
+
export type InstanceRouteSkeletonOnSaveHandler = (values: {
|
|
46
|
+
locales: string[];
|
|
47
|
+
entity: SerializedEntityDecl;
|
|
48
|
+
instanceId: string | undefined;
|
|
49
|
+
instanceContent: unknown;
|
|
50
|
+
customId: string;
|
|
51
|
+
isLocaleEntity: boolean | undefined;
|
|
52
|
+
childInstances: UnsafeEntityTaggedInstanceContainerWithChildInstances[];
|
|
53
|
+
route: LocationHook["route"];
|
|
54
|
+
setInstanceContent: Dispatch<SetStateAction<unknown>>;
|
|
55
|
+
setCustomId: Dispatch<SetStateAction<string>>;
|
|
56
|
+
getDeclFromDeclName: GetDeclFromDeclName;
|
|
57
|
+
updateLocalGitState?: () => Promise<void>;
|
|
58
|
+
}) => Promise<void>;
|
|
30
59
|
export type InstanceRouteSkeletonTitleBuilder = (values: {
|
|
31
60
|
locales: string[];
|
|
32
61
|
entity: SerializedEntityDecl;
|
|
@@ -43,6 +72,7 @@ type Props = {
|
|
|
43
72
|
init: InstanceRouteSkeletonInitializer;
|
|
44
73
|
titleBuilder: InstanceRouteSkeletonTitleBuilder;
|
|
45
74
|
onSubmit: InstanceRouteSkeletonOnSubmitHandler;
|
|
75
|
+
onSave: InstanceRouteSkeletonOnSaveHandler;
|
|
46
76
|
};
|
|
47
77
|
export declare const InstanceRouteSkeleton: FunctionalComponent<Props>;
|
|
48
78
|
export {};
|
|
@@ -24,7 +24,11 @@ const onBeforeUnload = (event) => {
|
|
|
24
24
|
// eslint-disable-next-line @typescript-eslint/no-deprecated -- best practice according to MDN
|
|
25
25
|
event.returnValue = "unsaved changes";
|
|
26
26
|
};
|
|
27
|
-
|
|
27
|
+
const applePlatformPattern = /(Mac|iPhone|iPod|iPad)/i;
|
|
28
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
29
|
+
const isApplePlatform = () => applePlatformPattern.test(window.navigator.platform);
|
|
30
|
+
const checkCmdOrCtrl = (event) => (isApplePlatform() ? event.metaKey : event.ctrlKey);
|
|
31
|
+
export const InstanceRouteSkeleton = ({ mode, buttons, init, titleBuilder, onSubmit, onSave, }) => {
|
|
28
32
|
const { params: { name, id }, } = useRoute();
|
|
29
33
|
const [locales] = useSetting("displayedLocales");
|
|
30
34
|
const [getDeclFromDeclName, declsLoaded] = useGetDeclFromDeclName();
|
|
@@ -38,6 +42,42 @@ export const InstanceRouteSkeleton = ({ mode, buttons, init, titleBuilder, onSub
|
|
|
38
42
|
const client = useContext(GitClientContext);
|
|
39
43
|
const { route } = useLocation();
|
|
40
44
|
const hasUnsavedChanges = useMemo(() => !deepEqual(instanceContent, savedInstanceContent), [instanceContent, savedInstanceContent]);
|
|
45
|
+
const saveHandler = useCallback((event) => {
|
|
46
|
+
if (checkCmdOrCtrl(event) && event.key === "s" && entity && instanceContent !== undefined) {
|
|
47
|
+
event.preventDefault();
|
|
48
|
+
runWithLoading(() => onSave({
|
|
49
|
+
locales,
|
|
50
|
+
entity,
|
|
51
|
+
instanceId: id,
|
|
52
|
+
instanceContent,
|
|
53
|
+
route,
|
|
54
|
+
customId,
|
|
55
|
+
getDeclFromDeclName,
|
|
56
|
+
isLocaleEntity,
|
|
57
|
+
setCustomId,
|
|
58
|
+
setInstanceContent: value => {
|
|
59
|
+
setInstanceContent(value);
|
|
60
|
+
setSavedInstanceContent(value);
|
|
61
|
+
},
|
|
62
|
+
childInstances,
|
|
63
|
+
updateLocalGitState: client?.updateLocalState,
|
|
64
|
+
})).catch((error) => {
|
|
65
|
+
console.error("Error submitting instance data:", error);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}, [
|
|
69
|
+
childInstances,
|
|
70
|
+
client?.updateLocalState,
|
|
71
|
+
customId,
|
|
72
|
+
entity,
|
|
73
|
+
getDeclFromDeclName,
|
|
74
|
+
id,
|
|
75
|
+
instanceContent,
|
|
76
|
+
isLocaleEntity,
|
|
77
|
+
locales,
|
|
78
|
+
onSave,
|
|
79
|
+
route,
|
|
80
|
+
]);
|
|
41
81
|
useEffect(() => {
|
|
42
82
|
if (hasUnsavedChanges) {
|
|
43
83
|
window.addEventListener("beforeunload", onBeforeUnload);
|
|
@@ -49,6 +89,17 @@ export const InstanceRouteSkeleton = ({ mode, buttons, init, titleBuilder, onSub
|
|
|
49
89
|
window.removeEventListener("beforeunload", onBeforeUnload);
|
|
50
90
|
};
|
|
51
91
|
}, [hasUnsavedChanges]);
|
|
92
|
+
useEffect(() => {
|
|
93
|
+
if (hasUnsavedChanges) {
|
|
94
|
+
window.addEventListener("keydown", saveHandler);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
window.removeEventListener("keydown", saveHandler);
|
|
98
|
+
}
|
|
99
|
+
return () => {
|
|
100
|
+
window.removeEventListener("keydown", saveHandler);
|
|
101
|
+
};
|
|
102
|
+
}, [hasUnsavedChanges, saveHandler]);
|
|
52
103
|
useEffect(() => {
|
|
53
104
|
document.title =
|
|
54
105
|
(entity && titleBuilder({ locales, entity, instanceContent, instanceId: id })) ??
|
|
@@ -11,32 +11,30 @@ const titleBuilder = ({ entity }) => {
|
|
|
11
11
|
const entityName = entity.name;
|
|
12
12
|
return "New " + toTitleCase(entityName) + " — TSONDB";
|
|
13
13
|
};
|
|
14
|
-
const
|
|
14
|
+
const submit = async ({ locales, entity, action, instanceContent, isLocaleEntity, customId, childInstances, setInstanceContent, setCustomId, route, getDeclFromDeclName, updateLocalGitState, }) => {
|
|
15
15
|
try {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
break;
|
|
39
|
-
}
|
|
16
|
+
const createdInstance = await createInstanceByEntityNameAndId(locales, entity.name, {
|
|
17
|
+
childInstances,
|
|
18
|
+
entityName: entity.name,
|
|
19
|
+
content: instanceContent,
|
|
20
|
+
id: undefined,
|
|
21
|
+
}, isLocaleEntity ? customId : undefined);
|
|
22
|
+
await updateLocalGitState?.();
|
|
23
|
+
switch (action) {
|
|
24
|
+
case "saveandcontinue": {
|
|
25
|
+
route(`/entities/${entity.name}/instances/${createdInstance.instance.id}`);
|
|
26
|
+
setInstanceContent(createdInstance.instance.content);
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
case "saveandaddanother": {
|
|
30
|
+
setInstanceContent(createTypeSkeleton(getDeclFromDeclName, entity.type));
|
|
31
|
+
setCustomId("");
|
|
32
|
+
alert(`Instance of entity ${entity.name} created successfully with identifier ${createdInstance.instance.id}. You can add another instance now.`);
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
case "save": {
|
|
36
|
+
route(`/entities/${entity.name}?created=${encodeURIComponent(createdInstance.instance.id)}`);
|
|
37
|
+
break;
|
|
40
38
|
}
|
|
41
39
|
}
|
|
42
40
|
}
|
|
@@ -46,8 +44,16 @@ const onSubmit = async ({ locales, entity, buttonName, instanceContent, isLocale
|
|
|
46
44
|
}
|
|
47
45
|
}
|
|
48
46
|
};
|
|
47
|
+
const onSubmit = async ({ buttonName, ...other }) => {
|
|
48
|
+
if (buttonName === "save" ||
|
|
49
|
+
buttonName === "saveandcontinue" ||
|
|
50
|
+
buttonName === "saveandaddanother") {
|
|
51
|
+
await submit({ ...other, action: buttonName });
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const onSave = other => submit({ ...other, action: "saveandcontinue" });
|
|
49
55
|
export const CreateInstance = () => (_jsx(InstanceRouteSkeleton, { mode: "create", buttons: [
|
|
50
56
|
{ label: "Save", name: "save", primary: true },
|
|
51
57
|
{ label: "Save and continue", name: "saveandcontinue" },
|
|
52
58
|
{ label: "Save and add another", name: "saveandaddanother" },
|
|
53
|
-
], init: init, titleBuilder: titleBuilder, onSubmit: onSubmit }));
|
|
59
|
+
], init: init, titleBuilder: titleBuilder, onSubmit: onSubmit, onSave: onSave }));
|
|
@@ -21,9 +21,9 @@ const titleBuilder = ({ locales, entity, instanceId, instanceContent, }) => {
|
|
|
21
21
|
}
|
|
22
22
|
return undefined;
|
|
23
23
|
};
|
|
24
|
-
const
|
|
24
|
+
const submit = async ({ action, locales, entity, instanceId, instanceContent, childInstances, route, updateLocalGitState, setInstanceContent, }) => {
|
|
25
25
|
try {
|
|
26
|
-
if (instanceId
|
|
26
|
+
if (instanceId) {
|
|
27
27
|
await updateInstanceByEntityNameAndId(locales, entity.name, instanceId, {
|
|
28
28
|
childInstances,
|
|
29
29
|
entityName: entity.name,
|
|
@@ -31,7 +31,7 @@ const onSubmit = async ({ locales, entity, instanceId, instanceContent, buttonNa
|
|
|
31
31
|
id: instanceId,
|
|
32
32
|
});
|
|
33
33
|
await updateLocalGitState?.();
|
|
34
|
-
switch (
|
|
34
|
+
switch (action) {
|
|
35
35
|
case "saveandcontinue": {
|
|
36
36
|
setInstanceContent(instanceContent);
|
|
37
37
|
break;
|
|
@@ -49,9 +49,15 @@ const onSubmit = async ({ locales, entity, instanceId, instanceContent, buttonNa
|
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
};
|
|
52
|
+
const onSubmit = async ({ buttonName, ...other }) => {
|
|
53
|
+
if (buttonName === "save" || buttonName === "saveandcontinue") {
|
|
54
|
+
await submit({ ...other, action: buttonName });
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
const onSave = other => submit({ ...other, action: "saveandcontinue" });
|
|
52
58
|
export const Instance = () => {
|
|
53
59
|
return (_jsx(InstanceRouteSkeleton, { mode: "edit", buttons: [
|
|
54
60
|
{ label: "Save", name: "save", primary: true },
|
|
55
61
|
{ label: "Save and continue", name: "saveandcontinue" },
|
|
56
|
-
], init: init, titleBuilder: titleBuilder, onSubmit: onSubmit }));
|
|
62
|
+
], init: init, titleBuilder: titleBuilder, onSubmit: onSubmit, onSave: onSave }));
|
|
57
63
|
};
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { createElement as _createElement } from "preact";
|
|
1
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "preact/jsx-runtime";
|
|
2
3
|
import { checkTableRowsAreSections, } from "../../shared/utils/markdown.js";
|
|
3
4
|
import { assertExhaustive } from "../../shared/utils/typeSafety.js";
|
|
4
5
|
import { InlineMarkdown } from "./InlineMarkdown.js";
|
|
5
|
-
export const BlockMarkdown = ({ node, outerHeadingLevel = 0, insertBefore, }) => {
|
|
6
|
+
export const BlockMarkdown = ({ node, outerHeadingLevel = 0, insertBefore, footnoteLabelSuffix = ")", }) => {
|
|
7
|
+
const inheritableProps = { outerHeadingLevel, footnoteLabelSuffix };
|
|
6
8
|
switch (node.kind) {
|
|
7
9
|
case "paragraph":
|
|
8
10
|
return (_jsxs("p", { children: [insertBefore, node.content.map((inline, ii) => (_jsx(InlineMarkdown, { node: inline }, ii)))] }));
|
|
@@ -19,13 +21,14 @@ export const BlockMarkdown = ({ node, outerHeadingLevel = 0, insertBefore, }) =>
|
|
|
19
21
|
case "table":
|
|
20
22
|
return (_jsxs(_Fragment, { children: [insertBefore, _jsxs("table", { children: [node.caption !== undefined && (_jsx("caption", { children: node.caption.map((inline, ci) => (_jsx(InlineMarkdown, { node: inline }, ci))) })), _jsx("thead", { children: _jsx(TableRow, { columns: node.columns, cells: node.header, cellType: "th" }) }), checkTableRowsAreSections(node.rows) ? (node.rows.map((section, si) => (_jsxs("tbody", { children: [section.header && (_jsx(TableRow, { columns: node.columns, cells: section.header, cellType: "th" })), section.rows.map((row, ri) => (_jsx(TableRow, { columns: node.columns, cells: row.cells }, ri)))] }, si)))) : (_jsx("tbody", { children: node.rows.map((row, ri) => (_jsx(TableRow, { columns: node.columns, cells: row.cells }, ri))) }))] })] }));
|
|
21
23
|
case "container":
|
|
22
|
-
return (_jsxs("div", { class: node.name, children: [insertBefore, node.content.map((childNode, i) => (
|
|
24
|
+
return (_jsxs("div", { class: node.name, children: [insertBefore, node.content.map((childNode, i) => (_createElement(BlockMarkdown, { ...inheritableProps, key: i, node: childNode })))] }));
|
|
23
25
|
case "footnote": {
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
+
const isNumeric = /^\d+$/.test(node.label);
|
|
27
|
+
const label = (_jsxs(_Fragment, { children: [_jsxs("span", { class: "footnote-label" + (isNumeric ? " footnote-label--numeric" : ""), "data-reference": node.label, children: [node.label, footnoteLabelSuffix] }), " "] }));
|
|
28
|
+
return (_jsxs("div", { role: "note", children: [insertBefore, node.content.map((n, i) => (_createElement(BlockMarkdown, { ...inheritableProps, key: i, node: n, insertBefore: label })))] }));
|
|
26
29
|
}
|
|
27
30
|
case "definitionList":
|
|
28
|
-
return (_jsxs(_Fragment, { children: [insertBefore, _jsx("dl", { children: node.content.map((item, ii) => (_jsxs("div", { children: [item.terms.map((term, ti) => (_jsx("dt", { children: term.map((inline, iii) => (_jsx(InlineMarkdown, { node: inline }, iii))) }, ti))), item.definitions.map((definition, di) => (_jsx("dd", { children: definition.map((n, i) => (
|
|
31
|
+
return (_jsxs(_Fragment, { children: [insertBefore, _jsx("dl", { children: node.content.map((item, ii) => (_jsxs("div", { children: [item.terms.map((term, ti) => (_jsx("dt", { children: term.map((inline, iii) => (_jsx(InlineMarkdown, { node: inline }, iii))) }, ti))), item.definitions.map((definition, di) => (_jsx("dd", { children: definition.map((n, i) => (_createElement(BlockMarkdown, { ...inheritableProps, key: i, node: n }))) }, di)))] }, ii))) })] }));
|
|
29
32
|
default:
|
|
30
33
|
return assertExhaustive(node);
|
|
31
34
|
}
|
|
@@ -22,8 +22,10 @@ export const InlineMarkdown = ({ node }) => {
|
|
|
22
22
|
const trailingNodes = node.content.slice(attributesEnd);
|
|
23
23
|
return (_jsxs("span", { class: "attributed", ...Object.fromEntries(Object.entries(node.attributes).map(([k, v]) => [`data-${k}`, v.toString()])), children: [leadingNodes.map((inline, i) => (_jsx(InlineMarkdown, { node: inline }, i))), Array.from({ length: count }, (_, i) => (_jsxs(Fragment, { children: [_jsx("span", { class: "attributed__name", children: _jsx(InlineMarkdown, { node: attributes[i * 4] ?? emptyNode }) }), _jsx("span", { class: "attributed__separator", children: _jsx(InlineMarkdown, { node: attributes[i * 4 + 1] ?? emptyNode }) }), _jsx("span", { class: "attributed__value", children: _jsx(InlineMarkdown, { node: attributes[i * 4 + 2] ?? emptyNode }) }), i < count - 1 && (_jsx("span", { class: "attributed__separator", children: _jsx(InlineMarkdown, { node: attributes[i * 4 + 3] ?? emptyNode }) }))] }, `attr-${(i + 1).toString()}`))), trailingNodes.map((inline, i) => (_jsx(InlineMarkdown, { node: inline }, i)))] }));
|
|
24
24
|
}
|
|
25
|
-
case "footnoteRef":
|
|
26
|
-
|
|
25
|
+
case "footnoteRef": {
|
|
26
|
+
const isNumeric = /^\d+$/.test(node.label);
|
|
27
|
+
return (_jsx("sup", { class: "footnote-ref" + (isNumeric ? " footnote-ref--numeric" : ""), "data-reference": node.label, children: node.label }));
|
|
28
|
+
}
|
|
27
29
|
case "text":
|
|
28
30
|
return node.content;
|
|
29
31
|
default:
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment } from "preact/jsx-runtime";
|
|
2
2
|
import { parseBlockMarkdown } from "../../shared/utils/markdown.js";
|
|
3
3
|
import { BlockMarkdown } from "./BlockMarkdown.js";
|
|
4
|
-
export const Markdown = ({ class: className, string, outerHeadingLevel, }) => {
|
|
4
|
+
export const Markdown = ({ class: className, string, outerHeadingLevel, footnoteLabelSuffix, }) => {
|
|
5
5
|
const blocks = parseBlockMarkdown(string);
|
|
6
|
-
const blockElements = blocks.map((block, i) => (_jsx(BlockMarkdown, { node: block, outerHeadingLevel: outerHeadingLevel }, `md-block-${i.toString()}`)));
|
|
6
|
+
const blockElements = blocks.map((block, i) => (_jsx(BlockMarkdown, { node: block, outerHeadingLevel: outerHeadingLevel, footnoteLabelSuffix: footnoteLabelSuffix }, `md-block-${i.toString()}`)));
|
|
7
7
|
if (className) {
|
|
8
8
|
return _jsx("div", { class: className, children: blockElements });
|
|
9
9
|
}
|