@fougere/svelte 0.16.2-alpha.0 → 0.16.4-alpha.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/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/useFormFor.d.ts +6 -4
- package/dist/useFormFor.d.ts.map +1 -1
- package/dist/useFormFor.js +23 -12
- package/dist/useFormFor.js.map +1 -1
- package/dist/useFougereData/CommandStore.d.ts +2 -1
- package/dist/useFougereData/CommandStore.d.ts.map +1 -1
- package/dist/useFougereData/CommandStore.js +1 -1
- package/dist/useFougereData/CommandStore.js.map +1 -1
- package/package.json +4 -4
- package/src/index.ts +2 -0
- package/src/useFormFor.ts +26 -12
- package/src/useFougereData/CommandStore.ts +5 -3
- package/template/scaffold.json +3 -1
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,8 @@ export { useCommand } from './useFougereData/CommandStore.js';
|
|
|
3
3
|
export { useQuery } from './useFougereData/QueryStore.js';
|
|
4
4
|
export { facade, facadeOf } from '@fougere/app/client';
|
|
5
5
|
export { useFormFor, type FormOptions } from './useFormFor.js';
|
|
6
|
+
export { cellOf, summaryOf, tableColumnsOf } from '@fougere/app/client';
|
|
6
7
|
export { useCurrentUser } from './useCurrentUser.js';
|
|
7
8
|
export { fetcher, CALL_ENDPOINT } from '@fougere/app/client';
|
|
8
|
-
export type { CallInput, EntityClass, FormEntity, FormField, SessionView, } from '@fougere/app/client';
|
|
9
|
+
export type { CallInput, EntityClass, FormEntity, FormField, SessionView, TableColumn, } from '@fougere/app/client';
|
|
9
10
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,wFAAwF;AACxF,OAAO,EAAE,UAAU,EAAE,MAAM,kCAAkC,CAAC;AAC9D,OAAO,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAE1D,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAE7D,YAAY,EACV,SAAS,EACT,WAAW,EACX,UAAU,EACV,SAAS,EACT,WAAW,GACZ,MAAM,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,wFAAwF;AACxF,OAAO,EAAE,UAAU,EAAE,MAAM,kCAAkC,CAAC;AAC9D,OAAO,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAE1D,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAE7D,YAAY,EACV,SAAS,EACT,WAAW,EACX,UAAU,EACV,SAAS,EACT,WAAW,EACX,WAAW,GACZ,MAAM,qBAAqB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ export { useQuery } from './useFougereData/QueryStore.js';
|
|
|
4
4
|
// The facade a page names, and the one an entity class names for a form.
|
|
5
5
|
export { facade, facadeOf } from '@fougere/app/client';
|
|
6
6
|
export { useFormFor } from './useFormFor.js';
|
|
7
|
+
export { cellOf, summaryOf, tableColumnsOf } from '@fougere/app/client';
|
|
7
8
|
export { useCurrentUser } from './useCurrentUser.js';
|
|
8
9
|
export { fetcher, CALL_ENDPOINT } from '@fougere/app/client';
|
|
9
10
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,wFAAwF;AACxF,OAAO,EAAE,UAAU,EAAE,MAAM,kCAAkC,CAAC;AAC9D,OAAO,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAC1D,yEAAyE;AACzE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,UAAU,EAAoB,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,wFAAwF;AACxF,OAAO,EAAE,UAAU,EAAE,MAAM,kCAAkC,CAAC;AAC9D,OAAO,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAC1D,yEAAyE;AACzE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,UAAU,EAAoB,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC"}
|
package/dist/useFormFor.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** The form contract — state, validation, submission, error mapping. */
|
|
2
2
|
import { type Readable, type Writable } from 'svelte/store';
|
|
3
|
-
import { type FormEntity, type FormField, type FormRow } from '@fougere/app/client';
|
|
3
|
+
import { type Choice, type FormEntity, type FormField, type FormFieldName, type FormRow } from '@fougere/app/client';
|
|
4
4
|
export interface FormOptions {
|
|
5
5
|
/** Command the submit rides. Default: 'create'. */
|
|
6
6
|
op?: string;
|
|
@@ -10,12 +10,14 @@ export interface FormOptions {
|
|
|
10
10
|
params?: Record<string, string>;
|
|
11
11
|
}
|
|
12
12
|
export declare function useFormFor<E extends FormEntity>(entity: E, options?: FormOptions): {
|
|
13
|
-
fields: FormField[];
|
|
13
|
+
fields: FormField<FormFieldName<E>>[];
|
|
14
14
|
/** The same fields, keyed by name — spread `fieldsByName.email.attrs` on an input
|
|
15
15
|
* and the page states no rule of its own. */
|
|
16
|
-
fieldsByName: Record<
|
|
16
|
+
fieldsByName: Partial<Record<FormFieldName<E>, FormField<FormFieldName<E>>>>;
|
|
17
17
|
values: Writable<Partial<FormRow<E>>>;
|
|
18
|
-
errors: Writable<Partial<Record<
|
|
18
|
+
errors: Writable<Partial<Record<FormFieldName<E>, string>>>;
|
|
19
|
+
choices: Writable<Partial<Record<FormFieldName<E>, Choice[]>>>;
|
|
20
|
+
search: (name: FormFieldName<E>, text?: string) => Promise<void>;
|
|
19
21
|
submit: () => Promise<FormRow<E> | null>;
|
|
20
22
|
validator: () => boolean;
|
|
21
23
|
valid: Readable<boolean>;
|
package/dist/useFormFor.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFormFor.d.ts","sourceRoot":"","sources":["../src/useFormFor.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,OAAO,EAA0B,KAAK,QAAQ,EAAE,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAC;AAEpF,OAAO,
|
|
1
|
+
{"version":3,"file":"useFormFor.d.ts","sourceRoot":"","sources":["../src/useFormFor.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,OAAO,EAA0B,KAAK,QAAQ,EAAE,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAC;AAEpF,OAAO,EAQL,KAAK,MAAM,EACX,KAAK,UAAU,EAEf,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,KAAK,OAAO,EAEb,MAAM,qBAAqB,CAAC;AAG7B,MAAM,WAAW,WAAW;IAC1B,mDAAmD;IACnD,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,qDAAqD;IACrD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,8DAA8D;IAC9D,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED,wBAAgB,UAAU,CAAC,CAAC,SAAS,UAAU,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,GAAE,WAAgB;;IAiDjF;kDAC8C;kBACkC,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;;;;mBAnCpH,aAAa,CAAC,CAAC,CAAC,oBAAc,OAAO,CAAC,IAAI,CAAC;kBAmB9C,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;qBAP7B,OAAO;;IA+B3B,sDAAsD;;EAGzD"}
|
package/dist/useFormFor.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** The form contract — state, validation, submission, error mapping. */
|
|
2
2
|
import { writable, derived, get } from 'svelte/store';
|
|
3
3
|
import { validationErrorsOf } from '@fougere/core/contract';
|
|
4
|
-
import { entityKeyOf, facadeOf, errorsByField, formFieldsOf, payloadOf, } from '@fougere/app/client';
|
|
4
|
+
import { Choices, entityKeyOf, fetcher, facadeOf, errorsByField, formFieldsOf, payloadOf, } from '@fougere/app/client';
|
|
5
5
|
import { useCommand } from './useFougereData/CommandStore.js';
|
|
6
6
|
export function useFormFor(entity, options = {}) {
|
|
7
7
|
const entityKey = entityKeyOf(entity);
|
|
@@ -13,6 +13,20 @@ export function useFormFor(entity, options = {}) {
|
|
|
13
13
|
// A form is designated by its ENTITY — it is a set of fields — so the facade it submits to is
|
|
14
14
|
// an address with no handler type behind it, and what it answers is the entity's row.
|
|
15
15
|
const command = useCommand(facadeOf(entity), options.op ?? 'create');
|
|
16
|
+
const choices = writable({});
|
|
17
|
+
/** What a reference field offers: its first rows, or those whose label contains `text`. */
|
|
18
|
+
async function search(name, text = '') {
|
|
19
|
+
const reference = fields.find((field) => field.name === name)?.reference;
|
|
20
|
+
if (!reference)
|
|
21
|
+
return;
|
|
22
|
+
const found = await Choices.of(fetcher, reference, text);
|
|
23
|
+
choices.update((current) => ({ ...current, [name]: found }));
|
|
24
|
+
}
|
|
25
|
+
if (typeof window !== 'undefined') {
|
|
26
|
+
for (const field of fields)
|
|
27
|
+
if (field.reference)
|
|
28
|
+
void search(field.name);
|
|
29
|
+
}
|
|
16
30
|
/** Local pre-verdict — same rules as the handler, saves a lost round-trip. */
|
|
17
31
|
function validator() {
|
|
18
32
|
const result = entity.validate(payloadOf(entity, get(values)));
|
|
@@ -22,17 +36,12 @@ export function useFormFor(entity, options = {}) {
|
|
|
22
36
|
async function submit() {
|
|
23
37
|
if (!validator())
|
|
24
38
|
return null;
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
errors.set(errorsByField(refusals));
|
|
32
|
-
return null;
|
|
33
|
-
}
|
|
34
|
-
throw err;
|
|
35
|
-
}
|
|
39
|
+
const answer = await command.execute({ params: options.params, input: payloadOf(entity, get(values)) });
|
|
40
|
+
const failure = get(command).error;
|
|
41
|
+
const refusals = failure && validationErrorsOf(failure);
|
|
42
|
+
if (refusals)
|
|
43
|
+
errors.set(errorsByField(refusals));
|
|
44
|
+
return answer;
|
|
36
45
|
}
|
|
37
46
|
const valid = derived(errors, ($errors) => Object.keys($errors).length === 0);
|
|
38
47
|
return {
|
|
@@ -42,6 +51,8 @@ export function useFormFor(entity, options = {}) {
|
|
|
42
51
|
fieldsByName: Object.fromEntries(fields.map((field) => [field.name, field])),
|
|
43
52
|
values,
|
|
44
53
|
errors,
|
|
54
|
+
choices,
|
|
55
|
+
search,
|
|
45
56
|
submit,
|
|
46
57
|
validator,
|
|
47
58
|
valid,
|
package/dist/useFormFor.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFormFor.js","sourceRoot":"","sources":["../src/useFormFor.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAgC,MAAM,cAAc,CAAC;AACpF,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EACL,WAAW,EACX,QAAQ,EACR,aAAa,EACb,YAAY,EACZ,SAAS,
|
|
1
|
+
{"version":3,"file":"useFormFor.js","sourceRoot":"","sources":["../src/useFormFor.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,7 +5,8 @@ export interface CommandStore<T, Refused extends ErrorCode = ErrorCode> extends
|
|
|
5
5
|
loading: boolean;
|
|
6
6
|
error: FougereError<Refused> | null;
|
|
7
7
|
}> {
|
|
8
|
-
|
|
8
|
+
/** The answer, or `null` when the call was refused — the refusal is in `error`, and nothing is thrown. */
|
|
9
|
+
execute(input?: CallInput): Promise<T | null>;
|
|
9
10
|
}
|
|
10
11
|
export declare function useCommand<Handler, Address extends string, Op extends keyof Handler & string>(facade: FacadeName<Handler, Address>, op: Op): CommandStore<Answer<Handler, Op>, Refused<Address, Op>>;
|
|
11
12
|
/** Read a store's current value without subscribing — used by `useFormFor`. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CommandStore.d.ts","sourceRoot":"","sources":["../../src/useFougereData/CommandStore.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,GAAG,EAAE,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtE,OAAO,EAA+F,KAAK,SAAS,EAAE,KAAK,MAAM,EAAE,KAAK,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAE9L,MAAM,WAAW,YAAY,CAAC,CAAC,EAAE,OAAO,SAAS,SAAS,GAAG,SAAS,CACpE,SAAQ,QAAQ,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,GAAG,IAAI,CAAA;CAAE,CAAC;IAC3E,OAAO,CAAC,KAAK,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"CommandStore.d.ts","sourceRoot":"","sources":["../../src/useFougereData/CommandStore.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,GAAG,EAAE,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtE,OAAO,EAA+F,KAAK,SAAS,EAAE,KAAK,MAAM,EAAE,KAAK,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAE9L,MAAM,WAAW,YAAY,CAAC,CAAC,EAAE,OAAO,SAAS,SAAS,GAAG,SAAS,CACpE,SAAQ,QAAQ,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,GAAG,IAAI,CAAA;CAAE,CAAC;IAC3E,0GAA0G;IAC1G,OAAO,CAAC,KAAK,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;CAC/C;AAED,wBAAgB,UAAU,CACxB,OAAO,EACP,OAAO,SAAS,MAAM,EACtB,EAAE,SAAS,MAAM,OAAO,GAAG,MAAM,EACjC,MAAM,EAAE,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,EAAE,EAAE,EAAE,GAAG,YAAY,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC,EAAE,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAuBvG;AAED,+EAA+E;AAC/E,OAAO,EAAE,GAAG,EAAE,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CommandStore.js","sourceRoot":"","sources":["../../src/useFougereData/CommandStore.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAiB,MAAM,cAAc,CAAC;AAE5D,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,QAAQ,EAA8D,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"CommandStore.js","sourceRoot":"","sources":["../../src/useFougereData/CommandStore.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAiB,MAAM,cAAc,CAAC;AAE5D,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,QAAQ,EAA8D,MAAM,qBAAqB,CAAC;AAQ9L,MAAM,UAAU,UAAU,CAIxB,MAAoC,EAAE,EAAM;IAC5C,MAAM,SAAS,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;IACpC,MAAM,KAAK,GAAG,QAAQ,CAAmD,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAE1G,OAAO;QACL,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,KAAK,CAAC,OAAO,CAAC,KAAiB;YAC7B,KAAK,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YAC1C,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,CAAC,MAAM,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC,CAAwB,CAAC;gBACzG,2EAA2E;gBAC3E,UAAU,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,CAAC;gBACnC,KAAK,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;gBAE3C,OAAO,MAAM,CAAC;YAChB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,OAAO,GAAG,cAAc,CAAC,GAAG,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;gBACnD,KAAK,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;gBAE9C,OAAO,IAAI,CAAC;YACd,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC;AAED,+EAA+E;AAC/E,OAAO,EAAE,GAAG,EAAE,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fougere/svelte",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.4-alpha.0",
|
|
4
4
|
"description": "Fougere's four primitives as Svelte stores — host-independent.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"fougere",
|
|
@@ -31,13 +31,13 @@
|
|
|
31
31
|
"template"
|
|
32
32
|
],
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@fougere/app": "0.16.
|
|
35
|
-
"@fougere/core": "0.16.
|
|
34
|
+
"@fougere/app": "0.16.4-alpha.0",
|
|
35
|
+
"@fougere/core": "0.16.4-alpha.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"svelte": "^5.53.4",
|
|
39
39
|
"vitest": "^4.1.0",
|
|
40
|
-
"@fougere/schema": "0.16.
|
|
40
|
+
"@fougere/schema": "0.16.4-alpha.0"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
43
|
"svelte": "^5.0.0"
|
package/src/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ export { useQuery } from './useFougereData/QueryStore.js';
|
|
|
4
4
|
// The facade a page names, and the one an entity class names for a form.
|
|
5
5
|
export { facade, facadeOf } from '@fougere/app/client';
|
|
6
6
|
export { useFormFor, type FormOptions } from './useFormFor.js';
|
|
7
|
+
export { cellOf, summaryOf, tableColumnsOf } from '@fougere/app/client';
|
|
7
8
|
export { useCurrentUser } from './useCurrentUser.js';
|
|
8
9
|
export { fetcher, CALL_ENDPOINT } from '@fougere/app/client';
|
|
9
10
|
|
|
@@ -13,4 +14,5 @@ export type {
|
|
|
13
14
|
FormEntity,
|
|
14
15
|
FormField,
|
|
15
16
|
SessionView,
|
|
17
|
+
TableColumn,
|
|
16
18
|
} from '@fougere/app/client';
|
package/src/useFormFor.ts
CHANGED
|
@@ -2,14 +2,18 @@
|
|
|
2
2
|
import { writable, derived, get, type Readable, type Writable } from 'svelte/store';
|
|
3
3
|
import { validationErrorsOf } from '@fougere/core/contract';
|
|
4
4
|
import {
|
|
5
|
+
Choices,
|
|
5
6
|
entityKeyOf,
|
|
7
|
+
fetcher,
|
|
6
8
|
facadeOf,
|
|
7
9
|
errorsByField,
|
|
8
10
|
formFieldsOf,
|
|
9
11
|
payloadOf,
|
|
12
|
+
type Choice,
|
|
10
13
|
type FormEntity,
|
|
11
14
|
type FormErrors,
|
|
12
15
|
type FormField,
|
|
16
|
+
type FormFieldName,
|
|
13
17
|
type FormRow,
|
|
14
18
|
type FormValues,
|
|
15
19
|
} from '@fougere/app/client';
|
|
@@ -26,7 +30,7 @@ export interface FormOptions {
|
|
|
26
30
|
|
|
27
31
|
export function useFormFor<E extends FormEntity>(entity: E, options: FormOptions = {}) {
|
|
28
32
|
const entityKey = entityKeyOf(entity);
|
|
29
|
-
const fields
|
|
33
|
+
const fields = formFieldsOf(entity, entityKey);
|
|
30
34
|
|
|
31
35
|
// `initial` wins over the declared default: editing a row shows the row. On a
|
|
32
36
|
// create form there is none, so the field opens on what is about to be written.
|
|
@@ -37,6 +41,19 @@ export function useFormFor<E extends FormEntity>(entity: E, options: FormOptions
|
|
|
37
41
|
// A form is designated by its ENTITY — it is a set of fields — so the facade it submits to is
|
|
38
42
|
// an address with no handler type behind it, and what it answers is the entity's row.
|
|
39
43
|
const command = useCommand(facadeOf(entity), options.op ?? 'create');
|
|
44
|
+
const choices = writable<Partial<Record<FormFieldName<E>, Choice[]>>>({});
|
|
45
|
+
|
|
46
|
+
/** What a reference field offers: its first rows, or those whose label contains `text`. */
|
|
47
|
+
async function search(name: FormFieldName<E>, text = ''): Promise<void> {
|
|
48
|
+
const reference = fields.find((field) => field.name === name)?.reference;
|
|
49
|
+
if (!reference) return;
|
|
50
|
+
const found = await Choices.of(fetcher, reference, text);
|
|
51
|
+
choices.update((current) => ({ ...current, [name]: found }));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (typeof window !== 'undefined') {
|
|
55
|
+
for (const field of fields) if (field.reference) void search(field.name);
|
|
56
|
+
}
|
|
40
57
|
|
|
41
58
|
/** Local pre-verdict — same rules as the handler, saves a lost round-trip. */
|
|
42
59
|
function validator(): boolean {
|
|
@@ -48,17 +65,12 @@ export function useFormFor<E extends FormEntity>(entity: E, options: FormOptions
|
|
|
48
65
|
|
|
49
66
|
async function submit(): Promise<FormRow<E> | null> {
|
|
50
67
|
if (!validator()) return null;
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if (refusals) {
|
|
56
|
-
errors.set(errorsByField<E>(refusals));
|
|
68
|
+
const answer = await command.execute({ params: options.params, input: payloadOf(entity, get(values)) });
|
|
69
|
+
const failure = get(command).error;
|
|
70
|
+
const refusals = failure && validationErrorsOf(failure);
|
|
71
|
+
if (refusals) errors.set(errorsByField<E>(refusals));
|
|
57
72
|
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
throw err;
|
|
61
|
-
}
|
|
73
|
+
return answer as FormRow<E> | null;
|
|
62
74
|
}
|
|
63
75
|
|
|
64
76
|
const valid: Readable<boolean> = derived(errors, ($errors) => Object.keys($errors).length === 0);
|
|
@@ -67,9 +79,11 @@ export function useFormFor<E extends FormEntity>(entity: E, options: FormOptions
|
|
|
67
79
|
fields,
|
|
68
80
|
/** The same fields, keyed by name — spread `fieldsByName.email.attrs` on an input
|
|
69
81
|
* and the page states no rule of its own. */
|
|
70
|
-
fieldsByName: Object.fromEntries(fields.map((field) => [field.name, field])) as Record<
|
|
82
|
+
fieldsByName: Object.fromEntries(fields.map((field) => [field.name, field])) as Partial<Record<FormFieldName<E>, FormField<FormFieldName<E>>>>,
|
|
71
83
|
values,
|
|
72
84
|
errors,
|
|
85
|
+
choices,
|
|
86
|
+
search,
|
|
73
87
|
submit,
|
|
74
88
|
validator,
|
|
75
89
|
valid,
|
|
@@ -4,7 +4,8 @@ import { asFougereError, callOf, addressOf, fetcher, invocationOf, mountedKeys,
|
|
|
4
4
|
|
|
5
5
|
export interface CommandStore<T, Refused extends ErrorCode = ErrorCode>
|
|
6
6
|
extends Readable<{ loading: boolean; error: FougereError<Refused> | null }> {
|
|
7
|
-
|
|
7
|
+
/** The answer, or `null` when the call was refused — the refusal is in `error`, and nothing is thrown. */
|
|
8
|
+
execute(input?: CallInput): Promise<T | null>;
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
export function useCommand<
|
|
@@ -17,7 +18,7 @@ export function useCommand<
|
|
|
17
18
|
|
|
18
19
|
return {
|
|
19
20
|
subscribe: store.subscribe,
|
|
20
|
-
async execute(input?: CallInput): Promise<Answer<Handler, Op
|
|
21
|
+
async execute(input?: CallInput): Promise<Answer<Handler, Op> | null> {
|
|
21
22
|
store.set({ loading: true, error: null });
|
|
22
23
|
try {
|
|
23
24
|
const result = (await sendCall(fetcher, callOf(facade, op), invocationOf(input))) as Answer<Handler, Op>;
|
|
@@ -29,7 +30,8 @@ export function useCommand<
|
|
|
29
30
|
} catch (err) {
|
|
30
31
|
const failure = asFougereError(err, entityKey, op);
|
|
31
32
|
store.set({ loading: false, error: failure });
|
|
32
|
-
|
|
33
|
+
|
|
34
|
+
return null;
|
|
33
35
|
}
|
|
34
36
|
},
|
|
35
37
|
};
|
package/template/scaffold.json
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"@fougere/schema": "latest",
|
|
16
16
|
"@fougere/defaults": "latest",
|
|
17
17
|
"@fougere/container": "latest",
|
|
18
|
+
"@fougere/log": "latest",
|
|
18
19
|
"better-sqlite3": "^13.0.3",
|
|
19
20
|
"kysely": "^0.28.17",
|
|
20
21
|
"@fougere/svelte": "latest"
|
|
@@ -27,5 +28,6 @@
|
|
|
27
28
|
"file": "vite.config.ts",
|
|
28
29
|
"from": "@fougere/vite",
|
|
29
30
|
"imported": "fougere"
|
|
30
|
-
}
|
|
31
|
+
},
|
|
32
|
+
"facade": { "kind": "kitAlias", "file": "vite.config.ts" }
|
|
31
33
|
}
|