alinea 0.5.11 → 0.6.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/chunks/{chunk-UUSWXVEM.js → chunk-3UKCBK2J.js} +1 -1
- package/dist/chunks/{chunk-MDIOFKJQ.js → chunk-533F6JLE.js} +21 -4
- package/dist/chunks/{chunk-I5C4WAC4.js → chunk-N5R3FLOP.js} +1 -1
- package/dist/cli/Serve.js +1 -1
- package/dist/cli/bin.js +1 -1
- package/dist/cli/generate/GenerateDashboard.js +1 -1
- package/dist/cloud/server/CloudAuthServer.js +1 -1
- package/dist/core/Document.js +2 -2
- package/dist/core/Field.d.ts +6 -1
- package/dist/core/Type.js +17 -0
- package/dist/core/shape/ListShape.js +4 -4
- package/dist/core/shape/RecordShape.js +3 -1
- package/dist/core/shape/RichTextShape.d.ts +3 -3
- package/dist/core/shape/RichTextShape.js +10 -6
- package/dist/dashboard/atoms/EntryEditorAtoms.js +24 -1
- package/dist/dashboard/atoms/FormAtoms.d.ts +8 -1
- package/dist/dashboard/atoms/FormAtoms.js +26 -10
- package/dist/dashboard/atoms/YAtom.js +5 -2
- package/dist/dashboard/editor/InputForm.js +1 -1
- package/dist/dashboard/editor/UseField.d.ts +2 -0
- package/dist/dashboard/editor/UseField.js +37 -2
- package/dist/dashboard/view/EntryEdit.js +1 -1
- package/dist/dashboard/view/InputLabel.d.ts +9 -25
- package/dist/dashboard/view/InputLabel.js +24 -13
- package/dist/index.css +52 -11
- package/dist/input/check/CheckField.browser.js +2 -1
- package/dist/input/check/CheckField.d.ts +0 -2
- package/dist/input/code/CodeField.browser.js +2 -1
- package/dist/input/code/CodeField.d.ts +0 -2
- package/dist/input/date/DateField.browser.js +2 -2
- package/dist/input/date/DateField.d.ts +0 -2
- package/dist/input/json/JsonField.browser.js +2 -1
- package/dist/input/json/JsonField.d.ts +0 -2
- package/dist/input/link/LinkField.browser.js +4 -4
- package/dist/input/link/LinkField.d.ts +0 -2
- package/dist/input/list/ListField.browser.js +14 -6
- package/dist/input/list/ListField.d.ts +0 -2
- package/dist/input/number/NumberField.browser.js +2 -2
- package/dist/input/number/NumberField.d.ts +0 -2
- package/dist/input/object/ObjectField.browser.js +3 -2
- package/dist/input/path/PathField.browser.js +2 -1
- package/dist/input/path/PathField.d.ts +0 -1
- package/dist/input/richtext/PickTextLink.js +1 -1
- package/dist/input/richtext/RichTextField.browser.js +390 -56
- package/dist/input/richtext/RichTextField.d.ts +0 -2
- package/dist/input/richtext/RichTextKit.js +2 -2
- package/dist/input/richtext/extensions/Link.js +1 -1
- package/dist/input/richtext/extensions/Small.js +1 -1
- package/dist/input/select/SelectField.browser.js +42 -38
- package/dist/input/select/SelectField.d.ts +0 -2
- package/dist/input/tabs/Tabs.browser.js +1 -1
- package/dist/input/text/TextField.browser.js +2 -1
- package/dist/input/text/TextField.d.ts +0 -2
- package/dist/picker/entry/EntryPicker.browser.js +1 -1
- package/dist/picker/url/UrlPicker.browser.js +1 -1
- package/dist/ui/util/TextareaAutosize.d.ts +2 -37
- package/dist/ui/util/TextareaAutosize.js +28 -258
- package/package.json +1 -1
- package/dist/chunks/chunk-OBYSELPT.js +0 -356
- package/dist/input/richtext/hook/UseEditor.d.ts +0 -4
- package/dist/input/richtext/hook/UseEditor.js +0 -22
|
@@ -12151,11 +12151,11 @@ var PasteRule = class {
|
|
|
12151
12151
|
this.handler = config.handler;
|
|
12152
12152
|
}
|
|
12153
12153
|
};
|
|
12154
|
-
var pasteRuleMatcherHandler = (text, find) => {
|
|
12154
|
+
var pasteRuleMatcherHandler = (text, find, event) => {
|
|
12155
12155
|
if (isRegExp(find)) {
|
|
12156
12156
|
return [...text.matchAll(find)];
|
|
12157
12157
|
}
|
|
12158
|
-
const matches2 = find(text);
|
|
12158
|
+
const matches2 = find(text, event);
|
|
12159
12159
|
if (!matches2) {
|
|
12160
12160
|
return [];
|
|
12161
12161
|
}
|
|
@@ -12187,7 +12187,7 @@ function run(config) {
|
|
|
12187
12187
|
const resolvedFrom = Math.max(from, pos);
|
|
12188
12188
|
const resolvedTo = Math.min(to, pos + node.content.size);
|
|
12189
12189
|
const textToMatch = node.textBetween(resolvedFrom - pos, resolvedTo - pos, void 0, "\uFFFC");
|
|
12190
|
-
const matches2 = pasteRuleMatcherHandler(textToMatch, rule.find);
|
|
12190
|
+
const matches2 = pasteRuleMatcherHandler(textToMatch, rule.find, pasteEvent);
|
|
12191
12191
|
matches2.forEach((match) => {
|
|
12192
12192
|
if (match.index === void 0) {
|
|
12193
12193
|
return;
|
|
@@ -12913,9 +12913,23 @@ var forEach = (items, fn) => (props) => {
|
|
|
12913
12913
|
var insertContent = (value, options) => ({ tr, commands: commands2 }) => {
|
|
12914
12914
|
return commands2.insertContentAt({ from: tr.selection.from, to: tr.selection.to }, value, options);
|
|
12915
12915
|
};
|
|
12916
|
+
var removeWhitespaces = (node) => {
|
|
12917
|
+
const children = node.childNodes;
|
|
12918
|
+
for (let i = children.length - 1; i >= 0; i -= 1) {
|
|
12919
|
+
const child = children[i];
|
|
12920
|
+
if (child.nodeType === 3 && child.nodeValue && !/\S/.test(child.nodeValue)) {
|
|
12921
|
+
node.removeChild(child);
|
|
12922
|
+
} else if (child.nodeType === 1) {
|
|
12923
|
+
removeWhitespaces(child);
|
|
12924
|
+
}
|
|
12925
|
+
}
|
|
12926
|
+
return node;
|
|
12927
|
+
};
|
|
12916
12928
|
function elementFromString(value) {
|
|
12917
12929
|
const wrappedValue = `<body>${value}</body>`;
|
|
12918
|
-
|
|
12930
|
+
const html = new window.DOMParser().parseFromString(wrappedValue, "text/html").body;
|
|
12931
|
+
removeWhitespaces(html);
|
|
12932
|
+
return removeWhitespaces(html);
|
|
12919
12933
|
}
|
|
12920
12934
|
function createNodeFromContent(content, schema, options) {
|
|
12921
12935
|
options = {
|
|
@@ -13355,6 +13369,9 @@ function getMarksBetween(from, to, doc3) {
|
|
|
13355
13369
|
});
|
|
13356
13370
|
} else {
|
|
13357
13371
|
doc3.nodesBetween(from, to, (node, pos) => {
|
|
13372
|
+
if (!node || node.nodeSize === void 0) {
|
|
13373
|
+
return;
|
|
13374
|
+
}
|
|
13358
13375
|
marks.push(...node.marks.map((mark) => ({
|
|
13359
13376
|
from: pos,
|
|
13360
13377
|
to: pos + node.nodeSize,
|
package/dist/cli/Serve.js
CHANGED
package/dist/cli/bin.js
CHANGED
package/dist/core/Document.js
CHANGED
|
@@ -13,8 +13,8 @@ function document(label, definition) {
|
|
|
13
13
|
return type(label, {
|
|
14
14
|
...tabs(
|
|
15
15
|
tab("Document", {
|
|
16
|
-
title: text("Title", { width: 0.5 }),
|
|
17
|
-
path: path("Path", { width: 0.5 }),
|
|
16
|
+
title: text("Title", { required: true, width: 0.5 }),
|
|
17
|
+
path: path("Path", { required: true, width: 0.5 }),
|
|
18
18
|
...definition,
|
|
19
19
|
[Meta]: {
|
|
20
20
|
icon: IcRoundInsertDriveFile
|
package/dist/core/Field.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type { ComponentType } from 'react';
|
|
|
4
4
|
import { Hint } from './Hint.js';
|
|
5
5
|
import { Shape } from './Shape.js';
|
|
6
6
|
export interface FieldOptions<Value> {
|
|
7
|
+
/** A description of the field */
|
|
7
8
|
label: string;
|
|
8
9
|
/** Hide this field in the dashboard */
|
|
9
10
|
hidden?: boolean;
|
|
@@ -11,8 +12,12 @@ export interface FieldOptions<Value> {
|
|
|
11
12
|
readOnly?: boolean;
|
|
12
13
|
/** The initial value of the field */
|
|
13
14
|
initialValue?: Value;
|
|
14
|
-
/** The value of this field is shared across all languages
|
|
15
|
+
/** The value of this field is shared across all languages */
|
|
15
16
|
shared?: boolean;
|
|
17
|
+
/** Providing a value for this field is required */
|
|
18
|
+
required?: boolean;
|
|
19
|
+
/** Validate the given value */
|
|
20
|
+
validate?(value: Value): boolean | string | undefined;
|
|
16
21
|
}
|
|
17
22
|
export type WithoutLabel<Options extends FieldOptions<any>> = Omit<Options, 'label'>;
|
|
18
23
|
export interface FieldMeta<Value, Mutator, Options extends FieldOptions<Value>> {
|
package/dist/core/Type.js
CHANGED
|
@@ -113,12 +113,29 @@ var TypeInstance = class {
|
|
|
113
113
|
this.sections.push(section({ definition: current }));
|
|
114
114
|
current = {};
|
|
115
115
|
};
|
|
116
|
+
const seen = /* @__PURE__ */ new Map();
|
|
117
|
+
function validateField(key, field) {
|
|
118
|
+
const ref = Field.ref(field);
|
|
119
|
+
if (!seen.has(ref))
|
|
120
|
+
return seen.set(ref, key);
|
|
121
|
+
const fieldLabel = Field.label(field);
|
|
122
|
+
throw new Error(
|
|
123
|
+
`Duplicate field "${fieldLabel}" in type "${label}", found under key "${key}" and "${seen.get(
|
|
124
|
+
ref
|
|
125
|
+
)}"
|
|
126
|
+
See: https://alinea.sh/docs/configuration/schema/type#fields-must-be-unique`
|
|
127
|
+
);
|
|
128
|
+
}
|
|
116
129
|
for (const [key, value] of entries(definition)) {
|
|
117
130
|
if (Field.isField(value)) {
|
|
118
131
|
current[key] = value;
|
|
132
|
+
validateField(key, value);
|
|
119
133
|
} else if (Section.isSection(value)) {
|
|
120
134
|
addCurrent();
|
|
121
135
|
this.sections.push(value);
|
|
136
|
+
for (const [key2, field] of entries(Section.fields(value))) {
|
|
137
|
+
validateField(key2, field);
|
|
138
|
+
}
|
|
122
139
|
}
|
|
123
140
|
}
|
|
124
141
|
addCurrent();
|
|
@@ -120,19 +120,19 @@ var ListShape = class {
|
|
|
120
120
|
parent.set(key, this.toY(this.create()));
|
|
121
121
|
}
|
|
122
122
|
watch(parent, key) {
|
|
123
|
-
const
|
|
123
|
+
const map = parent.get(key);
|
|
124
124
|
return (fun) => {
|
|
125
125
|
function w(events, transaction) {
|
|
126
126
|
for (const event of events) {
|
|
127
|
-
if (event.target ===
|
|
127
|
+
if (event.target === map)
|
|
128
128
|
fun();
|
|
129
129
|
if (event instanceof YMapEvent && event.keysChanged.has("index"))
|
|
130
130
|
fun();
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
|
-
|
|
133
|
+
map.observeDeep(w);
|
|
134
134
|
return () => {
|
|
135
|
-
|
|
135
|
+
map.unobserveDeep(w);
|
|
136
136
|
};
|
|
137
137
|
};
|
|
138
138
|
}
|
|
@@ -48,7 +48,9 @@ var RecordShape = class _RecordShape {
|
|
|
48
48
|
return void map.set(key, this.toY(value));
|
|
49
49
|
const self = value ?? {};
|
|
50
50
|
for (const key2 of keys(this.properties)) {
|
|
51
|
-
this.properties[key2].
|
|
51
|
+
this.properties[key2].init(current, key2);
|
|
52
|
+
if (key2 in self)
|
|
53
|
+
this.properties[key2].applyY(self[key2], current, key2);
|
|
52
54
|
}
|
|
53
55
|
}
|
|
54
56
|
init(parent, key) {
|
|
@@ -28,14 +28,14 @@ export declare class RichTextShape<Blocks> implements Shape<TextDoc<Blocks>, Ric
|
|
|
28
28
|
values: Record<string, RecordShape>;
|
|
29
29
|
constructor(label: Label, shapes?: Record<string, RecordShape<object>> | undefined, initialValue?: TextDoc<Blocks> | undefined, searchable?: boolean | undefined);
|
|
30
30
|
create(): TextDoc<Blocks>;
|
|
31
|
-
toXml(rows: TextDoc<Blocks>): (Y.XmlElement<{
|
|
31
|
+
toXml(rows: TextDoc<Blocks>): (Y.XmlText | Y.XmlElement<{
|
|
32
32
|
[key: string]: string;
|
|
33
|
-
}>
|
|
33
|
+
}>)[];
|
|
34
34
|
toY(value: TextDoc<Blocks>): Y.Map<unknown>;
|
|
35
35
|
fromY(value: Y.Map<any>): TextDoc<Blocks>;
|
|
36
36
|
applyY(value: TextDoc<Blocks>, parent: Y.Map<any>, key: string): void;
|
|
37
37
|
init(parent: Y.Map<any>, key: string): void;
|
|
38
|
-
watch(parent: Y.Map<any>, key: string): () => () => void;
|
|
38
|
+
watch(parent: Y.Map<any>, key: string): (fun: () => void) => () => void;
|
|
39
39
|
mutator(parent: Y.Map<any>, key: string): {
|
|
40
40
|
map: any;
|
|
41
41
|
fragment: any;
|
|
@@ -239,15 +239,19 @@ var RichTextShape = class {
|
|
|
239
239
|
parent.set(key, this.toY(this.create()));
|
|
240
240
|
}
|
|
241
241
|
watch(parent, key) {
|
|
242
|
-
|
|
242
|
+
const map = parent.get(key);
|
|
243
|
+
return (fun) => {
|
|
244
|
+
const listener = (events, tx) => {
|
|
245
|
+
if (tx.origin === "self")
|
|
246
|
+
return;
|
|
247
|
+
fun();
|
|
248
|
+
};
|
|
249
|
+
map.observeDeep(listener);
|
|
250
|
+
return () => map.unobserveDeep(listener);
|
|
243
251
|
};
|
|
244
252
|
}
|
|
245
253
|
mutator(parent, key) {
|
|
246
|
-
|
|
247
|
-
if (!map) {
|
|
248
|
-
parent.set(key, this.toY([]));
|
|
249
|
-
map = parent.get(key);
|
|
250
|
-
}
|
|
254
|
+
const map = parent.get(key);
|
|
251
255
|
return {
|
|
252
256
|
map,
|
|
253
257
|
fragment: map.get("$text"),
|
|
@@ -11,6 +11,7 @@ import "../../chunks/chunk-U5RRZUYZ.js";
|
|
|
11
11
|
import { Media } from "alinea/backend";
|
|
12
12
|
import {
|
|
13
13
|
EntryPhase,
|
|
14
|
+
Field,
|
|
14
15
|
ROOT_KEY,
|
|
15
16
|
Root,
|
|
16
17
|
Type,
|
|
@@ -311,7 +312,27 @@ function createEntryEditor(entryData) {
|
|
|
311
312
|
}
|
|
312
313
|
return res;
|
|
313
314
|
}
|
|
315
|
+
const errorsAtom = atom((get) => {
|
|
316
|
+
return get(get(form).errors);
|
|
317
|
+
});
|
|
318
|
+
const confirmErrorsAtom = atom(null, (get) => {
|
|
319
|
+
const errors = get(errorsAtom);
|
|
320
|
+
if (errors.size > 0) {
|
|
321
|
+
let errorMessage = "";
|
|
322
|
+
for (const [path, { field, error }] of errors.entries()) {
|
|
323
|
+
const label = Field.label(field);
|
|
324
|
+
const line = typeof error === "string" ? `${label}: ${error}` : label;
|
|
325
|
+
errorMessage += `
|
|
326
|
+
\u2014 ${line}`;
|
|
327
|
+
}
|
|
328
|
+
const message = `These fields contains errors, are you sure you want to publish?${errorMessage}`;
|
|
329
|
+
return confirm(message);
|
|
330
|
+
}
|
|
331
|
+
return true;
|
|
332
|
+
});
|
|
314
333
|
const publishEdits = atom(null, async (get, set) => {
|
|
334
|
+
if (!set(confirmErrorsAtom))
|
|
335
|
+
return;
|
|
315
336
|
const currentFile = entryFile(activeVersion);
|
|
316
337
|
const update = base64.stringify(edits.getLocalUpdate());
|
|
317
338
|
const entry = await getDraftEntry({ phase: EntryPhase.Published });
|
|
@@ -363,6 +384,8 @@ function createEntryEditor(entryData) {
|
|
|
363
384
|
});
|
|
364
385
|
});
|
|
365
386
|
const publishDraft = atom(null, async (get, set) => {
|
|
387
|
+
if (!set(confirmErrorsAtom))
|
|
388
|
+
return;
|
|
366
389
|
const mutations = [
|
|
367
390
|
{
|
|
368
391
|
type: MutationType.Publish,
|
|
@@ -533,7 +556,7 @@ function createEntryEditor(entryData) {
|
|
|
533
556
|
const form = atom((get) => {
|
|
534
557
|
const doc = get(currentDoc);
|
|
535
558
|
const readOnly = doc !== edits.doc ? true : void 0;
|
|
536
|
-
return new FormAtoms(type, doc.getMap(ROOT_KEY), { readOnly });
|
|
559
|
+
return new FormAtoms(type, doc.getMap(ROOT_KEY), "", { readOnly });
|
|
537
560
|
});
|
|
538
561
|
const yUpdate = debounceAtom(edits.yUpdate, 250);
|
|
539
562
|
const discardEdits = edits.resetChanges;
|
|
@@ -12,12 +12,19 @@ export interface FieldInfo<Value = any, Mutator = any, Options extends FieldOpti
|
|
|
12
12
|
export declare class FormAtoms<T = any> {
|
|
13
13
|
type: Type<T>;
|
|
14
14
|
container: Y.Map<any>;
|
|
15
|
+
path: string;
|
|
15
16
|
options: {
|
|
16
17
|
parent?: FormAtoms;
|
|
17
18
|
readOnly?: boolean;
|
|
18
19
|
};
|
|
19
20
|
private fields;
|
|
20
|
-
|
|
21
|
+
private errorMap;
|
|
22
|
+
errors: import("jotai").WritableAtom<Map<string, {
|
|
23
|
+
field: Field;
|
|
24
|
+
error: boolean | string;
|
|
25
|
+
}>, [path: string, field: Field<any, any, FieldOptions<any>>, error: string | boolean | undefined], void>;
|
|
26
|
+
hasErrors: Atom<boolean>;
|
|
27
|
+
constructor(type: Type<T>, container: Y.Map<any>, path?: string, options?: {
|
|
21
28
|
parent?: FormAtoms;
|
|
22
29
|
readOnly?: boolean;
|
|
23
30
|
});
|
|
@@ -5,8 +5,7 @@ import {
|
|
|
5
5
|
atom
|
|
6
6
|
} from "../../chunks/chunk-OBOPLPUQ.js";
|
|
7
7
|
import {
|
|
8
|
-
Doc
|
|
9
|
-
YMap
|
|
8
|
+
Doc
|
|
10
9
|
} from "../../chunks/chunk-OYP4EJOA.js";
|
|
11
10
|
import "../../chunks/chunk-O6EXLFU2.js";
|
|
12
11
|
import "../../chunks/chunk-U5RRZUYZ.js";
|
|
@@ -25,9 +24,10 @@ import { entries } from "alinea/core/util/Objects";
|
|
|
25
24
|
import { createContext, useContext, useMemo } from "react";
|
|
26
25
|
import { jsx } from "react/jsx-runtime";
|
|
27
26
|
var FormAtoms = class {
|
|
28
|
-
constructor(type, container, options = {}) {
|
|
27
|
+
constructor(type, container, path = "", options = {}) {
|
|
29
28
|
this.type = type;
|
|
30
29
|
this.container = container;
|
|
30
|
+
this.path = path;
|
|
31
31
|
this.options = options;
|
|
32
32
|
const readOnly = options.readOnly;
|
|
33
33
|
const forcedOptions = typeof readOnly === "boolean" ? { readOnly } : {};
|
|
@@ -65,6 +65,26 @@ var FormAtoms = class {
|
|
|
65
65
|
}, "self");
|
|
66
66
|
}
|
|
67
67
|
fields = /* @__PURE__ */ new Map();
|
|
68
|
+
errorMap = atom(
|
|
69
|
+
/* @__PURE__ */ new Map()
|
|
70
|
+
);
|
|
71
|
+
errors = atom(
|
|
72
|
+
(get) => get(this.errorMap),
|
|
73
|
+
(get, set, path, field, error) => {
|
|
74
|
+
const current = get(this.errorMap);
|
|
75
|
+
if (!error && !current.has(path))
|
|
76
|
+
return;
|
|
77
|
+
const errors = new Map(current);
|
|
78
|
+
if (error)
|
|
79
|
+
errors.set(path, { field, error });
|
|
80
|
+
else
|
|
81
|
+
errors.delete(path);
|
|
82
|
+
set(this.errorMap, errors);
|
|
83
|
+
if (this.options.parent)
|
|
84
|
+
set(this.options.parent.errors, path, field, error);
|
|
85
|
+
}
|
|
86
|
+
);
|
|
87
|
+
hasErrors = atom((get) => get(this.errorMap).size > 0);
|
|
68
88
|
data() {
|
|
69
89
|
return Type.shape(this.type).fromY(this.container);
|
|
70
90
|
}
|
|
@@ -87,7 +107,7 @@ var FormAtoms = class {
|
|
|
87
107
|
g(revision);
|
|
88
108
|
const current = shape.fromY(this.container.get(key));
|
|
89
109
|
const next = tracker ? tracker(this.getter(g)) : current;
|
|
90
|
-
if (next !== current)
|
|
110
|
+
if (tracker && next !== current)
|
|
91
111
|
shape.applyY(next, this.container, key);
|
|
92
112
|
return next;
|
|
93
113
|
});
|
|
@@ -105,7 +125,6 @@ var FormAtoms = class {
|
|
|
105
125
|
const res = this.fields.get(Field.ref(field));
|
|
106
126
|
const label = Field.label(field);
|
|
107
127
|
if (!res) {
|
|
108
|
-
console.log(this.options);
|
|
109
128
|
if (this.options.parent)
|
|
110
129
|
return this.options.parent.fieldInfo(field);
|
|
111
130
|
throw new Error(`Field not found: ${label}`);
|
|
@@ -146,12 +165,9 @@ function FormRow({
|
|
|
146
165
|
const rowForm = useMemo(() => {
|
|
147
166
|
const key = form.keyOf(field);
|
|
148
167
|
const inner = form.container.get(key);
|
|
149
|
-
if (rowId) {
|
|
150
|
-
if (!inner.has(rowId))
|
|
151
|
-
inner.set(rowId, new YMap());
|
|
152
|
-
}
|
|
153
168
|
const row = rowId ? inner.get(rowId) : inner;
|
|
154
|
-
|
|
169
|
+
const path = form.path + `.${key}` + (rowId ? `[${rowId}]` : "");
|
|
170
|
+
return new FormAtoms(type, row, path, {
|
|
155
171
|
readOnly,
|
|
156
172
|
parent: form
|
|
157
173
|
});
|
|
@@ -7,8 +7,11 @@ import "../../chunks/chunk-U5RRZUYZ.js";
|
|
|
7
7
|
function yAtom(yType, get) {
|
|
8
8
|
const revision = atom(0);
|
|
9
9
|
revision.onMount = (setAtom) => {
|
|
10
|
-
const onChange = (
|
|
11
|
-
|
|
10
|
+
const onChange = (events, tx) => {
|
|
11
|
+
if (tx.origin === "self")
|
|
12
|
+
return;
|
|
13
|
+
setAtom((x) => x + 1);
|
|
14
|
+
};
|
|
12
15
|
yType.observeDeep(onChange);
|
|
13
16
|
return () => yType.unobserveDeep(onChange);
|
|
14
17
|
};
|
|
@@ -37,9 +37,9 @@ function MissingView({ field }) {
|
|
|
37
37
|
}
|
|
38
38
|
function InputField({ field }) {
|
|
39
39
|
const View = field[Field.Data].view;
|
|
40
|
+
const options = useFieldOptions(field);
|
|
40
41
|
if (!View)
|
|
41
42
|
return /* @__PURE__ */ jsx(MissingView, { field });
|
|
42
|
-
const options = useFieldOptions(field);
|
|
43
43
|
if (options.hidden)
|
|
44
44
|
return null;
|
|
45
45
|
return /* @__PURE__ */ jsx(ErrorBoundary, { children: /* @__PURE__ */ jsx(View, { field }) });
|
|
@@ -5,8 +5,10 @@ export declare function useField<Value, Mutator, Options extends FieldOptions<Va
|
|
|
5
5
|
options: Awaited<Options>;
|
|
6
6
|
value: Awaited<Value>;
|
|
7
7
|
mutator: Mutator;
|
|
8
|
+
error: string | boolean | undefined;
|
|
8
9
|
};
|
|
9
10
|
export declare function useFieldKey<Value, Mutator, Options extends FieldOptions<Value>>(field: Field<Value, Mutator, Options>): string;
|
|
10
11
|
export declare function useFieldOptions<Value, Mutator, Options extends FieldOptions<Value>>(field: Field<Value, Mutator, Options>): Awaited<Options> | Awaited<Options>;
|
|
12
|
+
export declare function useFieldError<Value, Mutator, Options extends FieldOptions<Value>>(field: Field<Value, Mutator, Options>): string | boolean | undefined;
|
|
11
13
|
export declare function useFieldValue<Value, Mutator, Options extends FieldOptions<Value>>(field: Field<Value, Mutator, Options>): Awaited<Value>;
|
|
12
14
|
export declare function useFieldMutator<Value, Mutator, Options extends FieldOptions<Value>>(field: Field<Value, Mutator, Options>): Mutator;
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
|
-
useAtomValue
|
|
2
|
+
useAtomValue,
|
|
3
|
+
useSetAtom
|
|
3
4
|
} from "../../chunks/chunk-WF77DMLN.js";
|
|
4
5
|
import "../../chunks/chunk-OBOPLPUQ.js";
|
|
5
6
|
import "../../chunks/chunk-U5RRZUYZ.js";
|
|
6
7
|
|
|
7
8
|
// src/dashboard/editor/UseField.tsx
|
|
9
|
+
import { useCallback, useEffect, useMemo } from "react";
|
|
8
10
|
import { useFormContext } from "../atoms/FormAtoms.js";
|
|
9
11
|
function useField(field) {
|
|
10
12
|
const atoms = useFormContext();
|
|
@@ -13,12 +15,14 @@ function useField(field) {
|
|
|
13
15
|
const value = useFieldValue(actual);
|
|
14
16
|
const mutator = useFieldMutator(actual);
|
|
15
17
|
const options = useFieldOptions(actual);
|
|
18
|
+
const error = useFieldError(actual);
|
|
16
19
|
return {
|
|
17
20
|
fieldKey,
|
|
18
21
|
label: options.label,
|
|
19
22
|
options,
|
|
20
23
|
value,
|
|
21
|
-
mutator
|
|
24
|
+
mutator,
|
|
25
|
+
error
|
|
22
26
|
};
|
|
23
27
|
}
|
|
24
28
|
function useFieldKey(field) {
|
|
@@ -31,6 +35,36 @@ function useFieldOptions(field) {
|
|
|
31
35
|
const atom = atoms.fieldInfo(field);
|
|
32
36
|
return useAtomValue(atom.options);
|
|
33
37
|
}
|
|
38
|
+
function useFieldError(field) {
|
|
39
|
+
const atoms = useFormContext();
|
|
40
|
+
const setError = useSetAtom(atoms.errors);
|
|
41
|
+
const value = useFieldValue(field);
|
|
42
|
+
const options = useFieldOptions(field);
|
|
43
|
+
const key = useFieldKey(field);
|
|
44
|
+
const fieldPath = atoms.path + "." + key;
|
|
45
|
+
const hasError = useCallback(
|
|
46
|
+
(value2) => {
|
|
47
|
+
if (options.validate) {
|
|
48
|
+
const validates = options.validate(value2);
|
|
49
|
+
if (typeof validates === "boolean")
|
|
50
|
+
return !validates;
|
|
51
|
+
return validates;
|
|
52
|
+
}
|
|
53
|
+
const isRequired = options.required;
|
|
54
|
+
const isEmpty = value2 === void 0 || value2 === null || value2 === "" || Array.isArray(value2) && value2.length === 0;
|
|
55
|
+
if (isRequired && isEmpty)
|
|
56
|
+
return true;
|
|
57
|
+
},
|
|
58
|
+
[options]
|
|
59
|
+
);
|
|
60
|
+
const error = useMemo(() => {
|
|
61
|
+
return hasError(value);
|
|
62
|
+
}, [hasError, value]);
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
setError(fieldPath, field, error);
|
|
65
|
+
}, [setError, fieldPath, field, error]);
|
|
66
|
+
return error;
|
|
67
|
+
}
|
|
34
68
|
function useFieldValue(field) {
|
|
35
69
|
const atoms = useFormContext();
|
|
36
70
|
const atom = atoms.fieldInfo(field);
|
|
@@ -43,6 +77,7 @@ function useFieldMutator(field) {
|
|
|
43
77
|
}
|
|
44
78
|
export {
|
|
45
79
|
useField,
|
|
80
|
+
useFieldError,
|
|
46
81
|
useFieldKey,
|
|
47
82
|
useFieldMutator,
|
|
48
83
|
useFieldOptions,
|
|
@@ -205,7 +205,7 @@ function EntryEdit({ editor }) {
|
|
|
205
205
|
}
|
|
206
206
|
) }),
|
|
207
207
|
/* @__PURE__ */ jsx(EntryEditorProvider, { editor, children: /* @__PURE__ */ jsx(SuspenseBoundary, { name: "input form", children: mode === EditMode.Diff ? /* @__PURE__ */ jsx(ShowChanges, { editor }) : hasRootTabs && visibleTypes ? /* @__PURE__ */ jsx(Tabs.Panels, { children: visibleTypes.map((type, i) => {
|
|
208
|
-
return /* @__PURE__ */ jsx(FormProvider, { form, children: /* @__PURE__ */ jsx(Tabs.Panel, { tabIndex: i, children: /* @__PURE__ */ jsx(InputForm, { type }) }) }, i);
|
|
208
|
+
return /* @__PURE__ */ jsx(FormProvider, { form, children: /* @__PURE__ */ jsx(Tabs.Panel, { unmount: false, tabIndex: i, children: /* @__PURE__ */ jsx(InputForm, { type }) }) }, i);
|
|
209
209
|
}) }) : /* @__PURE__ */ jsx(VStack, { gap: 18, children: /* @__PURE__ */ jsx(InputForm, { form }) }) }) })
|
|
210
210
|
] })
|
|
211
211
|
] }),
|
|
@@ -1,21 +1,20 @@
|
|
|
1
|
-
import { Label } from 'alinea/core/Label';
|
|
2
1
|
import { ComponentType, PropsWithChildren } from 'react';
|
|
3
2
|
export type LabelHeaderProps = {
|
|
4
|
-
label:
|
|
3
|
+
label: string;
|
|
5
4
|
help?: string;
|
|
6
|
-
optional?: boolean;
|
|
7
5
|
size?: 'small' | 'medium' | 'large';
|
|
8
6
|
focused?: boolean;
|
|
9
7
|
icon?: ComponentType;
|
|
10
8
|
shared?: boolean;
|
|
11
9
|
readOnly?: boolean;
|
|
10
|
+
required?: boolean;
|
|
11
|
+
error?: boolean | string;
|
|
12
12
|
};
|
|
13
13
|
export declare const LabelHeader: import("react").NamedExoticComponent<LabelHeaderProps>;
|
|
14
|
-
export
|
|
15
|
-
label?:
|
|
14
|
+
export interface LabelProps extends PropsWithChildren {
|
|
15
|
+
label?: string;
|
|
16
16
|
asLabel?: boolean;
|
|
17
17
|
help?: string;
|
|
18
|
-
optional?: boolean;
|
|
19
18
|
width?: number;
|
|
20
19
|
inline?: boolean;
|
|
21
20
|
collection?: boolean;
|
|
@@ -26,23 +25,8 @@ export type LabelProps = PropsWithChildren<{
|
|
|
26
25
|
shared?: boolean;
|
|
27
26
|
readOnly?: boolean;
|
|
28
27
|
className?: string;
|
|
29
|
-
|
|
28
|
+
error?: boolean | string;
|
|
29
|
+
required?: boolean;
|
|
30
|
+
}
|
|
30
31
|
/** Label for an input */
|
|
31
|
-
export declare const InputLabel: import("react").ForwardRefExoticComponent<
|
|
32
|
-
label?: string | undefined;
|
|
33
|
-
asLabel?: boolean | undefined;
|
|
34
|
-
help?: string | undefined;
|
|
35
|
-
optional?: boolean | undefined;
|
|
36
|
-
width?: number | undefined;
|
|
37
|
-
inline?: boolean | undefined;
|
|
38
|
-
collection?: boolean | undefined;
|
|
39
|
-
focused?: boolean | undefined;
|
|
40
|
-
size?: "small" | "medium" | "large" | undefined;
|
|
41
|
-
icon?: ComponentType | undefined;
|
|
42
|
-
empty?: boolean | undefined;
|
|
43
|
-
shared?: boolean | undefined;
|
|
44
|
-
readOnly?: boolean | undefined;
|
|
45
|
-
className?: string | undefined;
|
|
46
|
-
} & {
|
|
47
|
-
children?: import("react").ReactNode;
|
|
48
|
-
} & import("react").RefAttributes<HTMLElement>>;
|
|
32
|
+
export declare const InputLabel: import("react").ForwardRefExoticComponent<LabelProps & import("react").RefAttributes<HTMLElement>>;
|