@sevenfold/setto-client 0.6.1 → 0.7.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.
@@ -0,0 +1,112 @@
1
+ import type { i18n as I18nType, TFunction } from 'i18next';
2
+ /**
3
+ * What a form is made of, and where that lives.
4
+ *
5
+ * A form's fields are content, not code. They sit in the locale bundle under
6
+ * `form.<formId>.fields.<n>` as an ordinary Setto list — the same shape a
7
+ * repeater uses — so the editor can add, remove, reorder and relabel them
8
+ * with the draft store it already has, and publishing writes them to
9
+ * `no.json` through the path that already exists. Nothing new has to be
10
+ * stored, fetched or deployed for a form to change.
11
+ *
12
+ * "form": {
13
+ * "kontakt": {
14
+ * "submit": "Send", "success": "Takk! …", "error": "Noe gikk galt.",
15
+ * "fields": {
16
+ * "1": { "name": "navn", "type": "text", "label": "Navn", "required": "true" },
17
+ * "2": { "name": "epost", "type": "email", "label": "E-post", "required": "true" },
18
+ * "3": { "name": "tjeneste", "type": "select", "label": "Hva gjelder det?",
19
+ * "options": "Tak\nVindu\nAnnet" },
20
+ * "4": { "name": "melding", "type": "textarea", "label": "Melding" }
21
+ * }
22
+ * }
23
+ * }
24
+ *
25
+ * Every value is a string because every locale value is: `required` is
26
+ * `"true"`, `options` is one per line. `label`, `placeholder` and `options`
27
+ * are per language; `name`, `type` and `required` are the same in all of them.
28
+ */
29
+ export declare const FORM_FIELD_TYPES: readonly ["text", "email", "tel", "number", "url", "date", "textarea", "select", "radio", "checkbox", "checkboxes"];
30
+ export type FormFieldType = (typeof FORM_FIELD_TYPES)[number];
31
+ /** What the editor calls each type. */
32
+ export declare const FORM_FIELD_TYPE_LABELS: Record<FormFieldType, string>;
33
+ /** Field props that are the same in every language. */
34
+ export declare const STRUCTURAL_FIELD_PROPS: readonly ["name", "type", "required"];
35
+ export declare function isFormFieldType(value: unknown): value is FormFieldType;
36
+ export declare function fieldTypeHasOptions(type: FormFieldType): boolean;
37
+ export declare function fieldTypeHasPlaceholder(type: FormFieldType): boolean;
38
+ /** A field as the form renders it, whichever source it came from. */
39
+ export interface ResolvedFormField {
40
+ /** Item key under `form.<id>.fields` — absent when the field came from props. */
41
+ itemKey?: string;
42
+ name: string;
43
+ type: FormFieldType;
44
+ /** i18n key the label is rendered (and inline-edited) through. */
45
+ labelKey: string;
46
+ label: string;
47
+ placeholder: string;
48
+ required: boolean;
49
+ options: string[];
50
+ }
51
+ export declare function formFieldsPrefix(formId: string): string;
52
+ export declare function parseOptions(value: string | undefined): string[];
53
+ /**
54
+ * Fields as the locale bundle defines them, or null when no loaded language
55
+ * has a `fields` list for this form and it should fall back to whatever the
56
+ * host passed. The current language is read first, then i18next's fallback
57
+ * chain, so a second language that has not translated the form still gets
58
+ * its structure. Items without a name are skipped: they are the empty husks a
59
+ * half-finished edit can leave behind, not fields.
60
+ */
61
+ export declare function readLocaleFields(i18n: I18nType, lng: string, formId: string, ns?: string): ResolvedFormField[] | null;
62
+ /** The host-supplied shape a form can still be given as a prop. */
63
+ export interface FormFieldProp {
64
+ /** Submitted form field name. */
65
+ name: string;
66
+ type: FormFieldType;
67
+ required?: boolean;
68
+ /** i18n key for the field label. */
69
+ labelKey: string;
70
+ /** i18n key for the placeholder text. */
71
+ placeholderKey?: string;
72
+ /** i18n key for the options of a select/radio/checkboxes field, one per line. */
73
+ optionsKey?: string;
74
+ }
75
+ export declare function resolvePropFields(fields: FormFieldProp[], t: TFunction): ResolvedFormField[];
76
+ /** `Hva gjelder det?` → `hva-gjelder-det`; falls back to `felt`. */
77
+ export declare function slugifyFieldName(label: string): string;
78
+ /** A name no existing field uses. */
79
+ export declare function uniqueFieldName(base: string, taken: Iterable<string>): string;
80
+ export type FormValue = string | boolean | string[];
81
+ export type FormValues = Record<string, FormValue>;
82
+ export declare function emptyValue(type: FormFieldType): FormValue;
83
+ export declare function emptyValues(fields: ResolvedFormField[]): FormValues;
84
+ export type FieldErrorCode = 'required' | 'email' | 'url' | 'number';
85
+ /** Norwegian fallbacks; a site overrides them via `form.validation.<code>`. */
86
+ export declare const FIELD_ERROR_DEFAULTS: Record<FieldErrorCode, string>;
87
+ export declare function validateValues(fields: ResolvedFormField[], values: FormValues): Record<string, FieldErrorCode>;
88
+ /**
89
+ * The body `SettoForm` posts to setto-server. See `form-submission.ts` there.
90
+ *
91
+ * `hp` and `elapsed` are the bot signals: the honeypot's value (a person
92
+ * cannot see the field, so anything in it came from a script) and the
93
+ * milliseconds between the visitor's first touch of the form and submit
94
+ * (a script fills a form in tens of milliseconds; a person cannot).
95
+ */
96
+ export interface SubmissionEnvelope {
97
+ data: FormValues;
98
+ labels: Record<string, string>;
99
+ order: string[];
100
+ title?: string;
101
+ page?: string;
102
+ hp: string;
103
+ elapsed: number;
104
+ }
105
+ export declare function buildEnvelope(fields: ResolvedFormField[], values: FormValues, extra: {
106
+ title?: string;
107
+ page?: string;
108
+ hp: string;
109
+ elapsed: number;
110
+ }): SubmissionEnvelope;
111
+ /** Where a site's forms go unless the host says otherwise. */
112
+ export declare function settoSubmissionUrl(apiUrl: string, siteId: string, formId: string): string;
@@ -0,0 +1,12 @@
1
+ import type { SettoModule } from '../module';
2
+ /**
3
+ * The forms module: a form primitive the site renders, an inbox in the
4
+ * dashboard, and the api segment between them. Server side lives in
5
+ * setto-server `src/modules/forms`.
6
+ */
7
+ export declare const formsModule: SettoModule;
8
+ export { SettoForm } from './SettoForm';
9
+ export type { SettoFormProps, SettoFormClassNames } from './SettoForm';
10
+ export type { FormSettings, FormSubmissionRow, FormsApi } from './api';
11
+ export { FORM_FIELD_TYPES, FORM_FIELD_TYPE_LABELS, formFieldsPrefix, isFormFieldType, readLocaleFields, settoSubmissionUrl, } from './fields';
12
+ export type { FormFieldProp, FormFieldType, FormValue, FormValues, ResolvedFormField, SubmissionEnvelope, } from './fields';
@@ -0,0 +1,5 @@
1
+ import type { SettoModule } from './module';
2
+ export type { SettoModule } from './module';
3
+ /** Every module the dashboard shows, in tab order. */
4
+ export declare const SETTO_MODULES: readonly SettoModule[];
5
+ export declare function findModule(id: string | null): SettoModule | undefined;
@@ -0,0 +1,30 @@
1
+ import type { ComponentType } from 'react';
2
+ /**
3
+ * What a Setto module is, from the client's side.
4
+ *
5
+ * A module is a capability a site gets by using the library — forms today;
6
+ * bookings, newsletters, reviews later. Each one is a directory under
7
+ * `src/modules/<id>` that owns its three faces:
8
+ *
9
+ * - a primitive the site renders (`SettoForm`), with edit chrome for the
10
+ * owner and content that lives in the locale so it publishes with the
11
+ * rest of the site;
12
+ * - a panel in the dashboard at `/setto?view=<id>` (`AdminView`), reached
13
+ * from the editor toolbar's settings item;
14
+ * - an api segment (`createFormsApi`) that `createApi` mounts under its id,
15
+ * against routes setto-server keeps under `src/modules/<id>`.
16
+ *
17
+ * Registering the module in `src/modules/index.ts` is what puts its tab in
18
+ * the dashboard. The primitive and the types are exported from the package
19
+ * root like everything else.
20
+ */
21
+ export interface SettoModule {
22
+ /** Stable id — also the dashboard view name and the api segment name. */
23
+ id: string;
24
+ /** Tab label in the dashboard, in Norwegian. */
25
+ label: string;
26
+ /** The dashboard panel for the current site. */
27
+ AdminView: ComponentType<{
28
+ siteId: string;
29
+ }>;
30
+ }