@fougere/svelte 0.16.3-alpha.0 → 0.16.5-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.
@@ -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 FormFieldName, 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;
@@ -16,6 +16,10 @@ export declare function useFormFor<E extends FormEntity>(entity: E, options?: Fo
16
16
  fieldsByName: Partial<Record<FormFieldName<E>, FormField<FormFieldName<E>>>>;
17
17
  values: Writable<Partial<FormRow<E>>>;
18
18
  errors: Writable<Partial<Record<FormFieldName<E>, string>>>;
19
+ choices: Writable<Partial<Record<FormFieldName<E>, Choice[]>>>;
20
+ /** Per reference field: its target holds more rows than one load offers, so a search is worth showing. */
21
+ searchable: Writable<Partial<Record<FormFieldName<E>, boolean>>>;
22
+ search: (name: FormFieldName<E>, text?: string) => Promise<void>;
19
23
  submit: () => Promise<FormRow<E> | null>;
20
24
  validator: () => boolean;
21
25
  valid: Readable<boolean>;
@@ -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,EAML,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;;IAoCjF;kDAC8C;kBACkC,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;;;kBAhBvH,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;qBAP7B,OAAO;;IA6B3B,sDAAsD;;EAGzD"}
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;;IAmDjF;kDAC8C;kBACkC,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;;;;IAI9I,0GAA0G;;mBAxChF,aAAa,CAAC,CAAC,CAAC,oBAAc,OAAO,CAAC,IAAI,CAAC;kBAoB9C,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;qBAP7B,OAAO;;IAiC3B,sDAAsD;;EAGzD"}
@@ -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,23 @@ 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
+ const searchable = writable({});
18
+ /** What a reference field offers: its first rows, or those whose label contains `text`. */
19
+ async function search(name, text = '') {
20
+ const reference = fields.find((field) => field.name === name)?.reference;
21
+ if (!reference)
22
+ return;
23
+ const offer = await Choices.of(fetcher, reference, text);
24
+ choices.update((current) => ({ ...current, [name]: offer.choices }));
25
+ if (!text)
26
+ searchable.update((current) => ({ ...current, [name]: offer.more }));
27
+ }
28
+ if (typeof window !== 'undefined') {
29
+ for (const field of fields)
30
+ if (field.reference)
31
+ void search(field.name);
32
+ }
16
33
  /** Local pre-verdict — same rules as the handler, saves a lost round-trip. */
17
34
  function validator() {
18
35
  const result = entity.validate(payloadOf(entity, get(values)));
@@ -37,6 +54,10 @@ export function useFormFor(entity, options = {}) {
37
54
  fieldsByName: Object.fromEntries(fields.map((field) => [field.name, field])),
38
55
  values,
39
56
  errors,
57
+ choices,
58
+ /** Per reference field: its target holds more rows than one load offers, so a search is worth showing. */
59
+ searchable,
60
+ search,
40
61
  submit,
41
62
  validator,
42
63
  valid,
@@ -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,GAOV,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,kCAAkC,CAAC;AAW9D,MAAM,UAAU,UAAU,CAAuB,MAAS,EAAE,OAAO,GAAgB,EAAE;IACnF,MAAM,SAAS,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;IACtC,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAE/C,8EAA8E;IAC9E,gFAAgF;IAChF,MAAM,MAAM,GAA4B,QAAQ,CAC9C,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAkB,CACzH,CAAC;IACF,MAAM,MAAM,GAAG,QAAQ,CAAgB,EAAE,CAAC,CAAC;IAC3C,8FAA8F;IAC9F,sFAAsF;IACtF,MAAM,OAAO,GAAG,UAAU,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,EAAE,IAAI,QAAQ,CAAC,CAAC;IAErE,8EAA8E;IAC9E,SAAS,SAAS;QAChB,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAC/D,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,aAAa,CAAI,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;QAElE,OAAO,MAAM,CAAC,OAAO,CAAC;IACxB,CAAC;IAED,KAAK,UAAU,MAAM;QACnB,IAAI,CAAC,SAAS,EAAE;YAAE,OAAO,IAAI,CAAC;QAC9B,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC;QACxG,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC;QACnC,MAAM,QAAQ,GAAG,OAAO,IAAI,kBAAkB,CAAC,OAAO,CAAC,CAAC;QACxD,IAAI,QAAQ;YAAE,MAAM,CAAC,GAAG,CAAC,aAAa,CAAI,QAAQ,CAAC,CAAC,CAAC;QAErD,OAAO,MAA2B,CAAC;IACrC,CAAC;IAED,MAAM,KAAK,GAAsB,OAAO,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC;IAEjG,OAAO;QACL,MAAM;QACN;sDAC8C;QAC9C,YAAY,EAAE,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAmE;QAC9I,MAAM;QACN,MAAM;QACN,MAAM;QACN,SAAS;QACT,KAAK;QACL,sDAAsD;QACtD,OAAO;KACR,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"useFormFor.js","sourceRoot":"","sources":["../src/useFormFor.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fougere/svelte",
3
- "version": "0.16.3-alpha.0",
3
+ "version": "0.16.5-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.3-alpha.0",
35
- "@fougere/core": "0.16.3-alpha.0"
34
+ "@fougere/app": "0.16.5-alpha.0",
35
+ "@fougere/core": "0.16.5-alpha.0"
36
36
  },
37
37
  "devDependencies": {
38
38
  "svelte": "^5.53.4",
39
39
  "vitest": "^4.1.0",
40
- "@fougere/schema": "0.16.3-alpha.0"
40
+ "@fougere/schema": "0.16.5-alpha.0"
41
41
  },
42
42
  "peerDependencies": {
43
43
  "svelte": "^5.0.0"
package/src/useFormFor.ts CHANGED
@@ -2,11 +2,14 @@
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,
@@ -38,6 +41,21 @@ export function useFormFor<E extends FormEntity>(entity: E, options: FormOptions
38
41
  // A form is designated by its ENTITY — it is a set of fields — so the facade it submits to is
39
42
  // an address with no handler type behind it, and what it answers is the entity's row.
40
43
  const command = useCommand(facadeOf(entity), options.op ?? 'create');
44
+ const choices = writable<Partial<Record<FormFieldName<E>, Choice[]>>>({});
45
+ const searchable = writable<Partial<Record<FormFieldName<E>, boolean>>>({});
46
+
47
+ /** What a reference field offers: its first rows, or those whose label contains `text`. */
48
+ async function search(name: FormFieldName<E>, text = ''): Promise<void> {
49
+ const reference = fields.find((field) => field.name === name)?.reference;
50
+ if (!reference) return;
51
+ const offer = await Choices.of(fetcher, reference, text);
52
+ choices.update((current) => ({ ...current, [name]: offer.choices }));
53
+ if (!text) searchable.update((current) => ({ ...current, [name]: offer.more }));
54
+ }
55
+
56
+ if (typeof window !== 'undefined') {
57
+ for (const field of fields) if (field.reference) void search(field.name);
58
+ }
41
59
 
42
60
  /** Local pre-verdict — same rules as the handler, saves a lost round-trip. */
43
61
  function validator(): boolean {
@@ -66,6 +84,10 @@ export function useFormFor<E extends FormEntity>(entity: E, options: FormOptions
66
84
  fieldsByName: Object.fromEntries(fields.map((field) => [field.name, field])) as Partial<Record<FormFieldName<E>, FormField<FormFieldName<E>>>>,
67
85
  values,
68
86
  errors,
87
+ choices,
88
+ /** Per reference field: its target holds more rows than one load offers, so a search is worth showing. */
89
+ searchable,
90
+ search,
69
91
  submit,
70
92
  validator,
71
93
  valid,
@@ -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"