@wssto2/vue-core 0.17.0 → 0.19.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,27 @@
1
+ import type { Extension } from "@codemirror/state";
2
+ /** What `CodeField` edits. `liquid` is HTML with `{{ }}` and `{% %}` (and the `<script>` / `<style>` of the page highlighted as their own languages). */
3
+ export type CodeLanguage = "liquid" | "html" | "css" | "javascript";
4
+ /** A finding of the server's linter (`site lint-templates`) shown in the editor: the gutter and an underline. Lines and columns count from 1. */
5
+ export interface CodeMarker {
6
+ line: number;
7
+ /** Without it the whole line is marked. */
8
+ column?: number;
9
+ severity: "error" | "warning";
10
+ message: string;
11
+ }
12
+ /**
13
+ * CodeMirror 6, every package an optional peer dependency (`npm i @codemirror/state @codemirror/view @codemirror/commands
14
+ * @codemirror/language @codemirror/lint`, and `@codemirror/lang-liquid` / `lang-html` / `lang-css` / `lang-javascript` for the
15
+ * languages in use). This file is the only place that imports them and only through `import()`: nothing loads until a `CodeField` shows.
16
+ */
17
+ export declare function loadCodeEditor(): Promise<{
18
+ state: typeof import("@codemirror/state");
19
+ view: typeof import("@codemirror/view");
20
+ commands: typeof import("@codemirror/commands");
21
+ language: typeof import("@codemirror/language");
22
+ lint: typeof import("@codemirror/lint");
23
+ highlight: typeof import("@lezer/highlight");
24
+ }>;
25
+ export type CodeEditor = Awaited<ReturnType<typeof loadCodeEditor>>;
26
+ /** The grammar of one language, loaded when it is first used. */
27
+ export declare function loadCodeLanguage(name: CodeLanguage): Promise<Extension>;
@@ -0,0 +1,46 @@
1
+ import { ErrorBag } from "../errors";
2
+ import type { FieldMessages } from "../validation";
3
+ import { type LocaleMode } from "./localeMode";
4
+ import type { ResourceSource } from "./resources";
5
+ import type { FormSchema } from "./schema";
6
+ /**
7
+ * A form generated from a typed schema (a content type's fields): each field's `kind` picks the control. It generates a form,
8
+ * nothing more: no page, no save; put it in an `EditorPage` and save the value yourself.
9
+ *
10
+ * <DynamicForm v-model="values" :schema="schema" :locales="['hr', 'en']" :errors="form.errors.all()" :resources="media" />
11
+ *
12
+ * - **Value:** `Record<field key, value>`. A translatable text is `Record<locale, string>`; an image / document is a resource id,
13
+ * a gallery a list of ids; a collection a list of row values.
14
+ * - **`visible_when`:** a field whose rule does not hold is not shown. It keeps its value, and an error under its key is not shown.
15
+ * - **`errors`:** go-core's field messages (`form.errors.all()`, or the `ErrorBag`), by field key; `images.0.title` reaches a row.
16
+ * - **`localeMode`:** `"perField"` (default) shows a language strip on each translatable field; `"form"` shows one switcher for
17
+ * the whole form, which switches every translatable field at once.
18
+ */
19
+ type __VLS_Props = {
20
+ schema: FormSchema;
21
+ modelValue?: Record<string, unknown>;
22
+ /** The languages of translatable fields. Default: the application's. */
23
+ locales?: readonly string[];
24
+ /** The language shown first, and the one a required translatable field needs. Default: the first. */
25
+ defaultLocale?: string;
26
+ errors?: FieldMessages | ErrorBag;
27
+ /** Reads the values instead of editing them. */
28
+ readonly?: boolean;
29
+ localeMode?: LocaleMode;
30
+ /** The media library of image, document and gallery fields, and of the picture button of rich text. */
31
+ resources?: ResourceSource;
32
+ };
33
+ declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
34
+ "update:modelValue": (values: Record<string, unknown>) => any;
35
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
36
+ "onUpdate:modelValue"?: ((values: Record<string, unknown>) => any) | undefined;
37
+ }>, {
38
+ errors: FieldMessages | ErrorBag;
39
+ modelValue: Record<string, unknown>;
40
+ readonly: boolean;
41
+ locales: readonly string[];
42
+ defaultLocale: string;
43
+ resources: ResourceSource;
44
+ localeMode: LocaleMode;
45
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
46
+ export default _default;
@@ -0,0 +1,47 @@
1
+ import { type FieldProps } from "../field";
2
+ import type { FieldMessages } from "../validation";
3
+ import type { LocaleMode } from "./localeMode";
4
+ import type { ResourceSource } from "./resources";
5
+ import type { SchemaField } from "./schema";
6
+ /**
7
+ * A repeatable group: rows the user adds, removes and moves, each a `DynamicForm` over `fields`. The value is the list of the
8
+ * rows' values. Errors of the rows arrive in the form's paths relative to this field (`0.title` is the first row's title).
9
+ *
10
+ * <RepeatField v-model="form.slides" :fields="slideFields" label="Slides" />
11
+ */
12
+ type __VLS_Props = FieldProps & {
13
+ /** The fields of each row. */
14
+ fields: readonly SchemaField[];
15
+ modelValue?: readonly Record<string, unknown>[];
16
+ locales?: readonly string[];
17
+ defaultLocale?: string;
18
+ /** Messages by path inside this field: `"0.title"`. */
19
+ errors?: FieldMessages;
20
+ resources?: ResourceSource;
21
+ addLabel?: string;
22
+ localeMode?: LocaleMode;
23
+ };
24
+ declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
25
+ "update:modelValue": (rows: Record<string, unknown>[]) => any;
26
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
27
+ "onUpdate:modelValue"?: ((rows: Record<string, unknown>[]) => any) | undefined;
28
+ }>, {
29
+ disabled: boolean;
30
+ name: string;
31
+ label: string;
32
+ error: string;
33
+ required: boolean;
34
+ presentation: "control" | "value";
35
+ errors: FieldMessages;
36
+ modelValue: readonly Record<string, unknown>[];
37
+ labelHidden: boolean;
38
+ hint: string;
39
+ editable: boolean;
40
+ lockedReason: string;
41
+ locales: readonly string[];
42
+ defaultLocale: string;
43
+ resources: ResourceSource;
44
+ addLabel: string;
45
+ localeMode: LocaleMode;
46
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
47
+ export default _default;
@@ -0,0 +1,38 @@
1
+ import { type FieldProps } from "../field";
2
+ import { type ResourceId, type ResourceSource } from "./resources";
3
+ /**
4
+ * One resource, or several, chosen from the app's media library: the value is an id (`multiple`: a list of ids). It shows each
5
+ * as a picture from `source.thumbnail` and opens a `PickerModal` over `source.list(query)` to choose. The app owns the library;
6
+ * uploads are its too.
7
+ *
8
+ * <ResourceField v-model="form.image" :source="media" label="Image" accept="image/*" />
9
+ * <ResourceField v-model="form.gallery" :source="media" multiple label="Gallery" />
10
+ */
11
+ type __VLS_Props = FieldProps & {
12
+ source: ResourceSource;
13
+ /** Several ids, kept in the order chosen. */
14
+ multiple?: boolean;
15
+ /** Passed to `source.list` as `query.accept`. */
16
+ accept?: string;
17
+ modelValue?: ResourceId | readonly ResourceId[] | null;
18
+ };
19
+ declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
20
+ "update:modelValue": (value: ResourceId | ResourceId[] | null) => any;
21
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
22
+ "onUpdate:modelValue"?: ((value: ResourceId | ResourceId[] | null) => any) | undefined;
23
+ }>, {
24
+ disabled: boolean;
25
+ name: string;
26
+ label: string;
27
+ error: string;
28
+ required: boolean;
29
+ presentation: "control" | "value";
30
+ accept: string;
31
+ modelValue: ResourceId | readonly ResourceId[] | null;
32
+ labelHidden: boolean;
33
+ hint: string;
34
+ editable: boolean;
35
+ lockedReason: string;
36
+ multiple: boolean;
37
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
38
+ export default _default;
@@ -0,0 +1,12 @@
1
+ import { type ResourceItem, type ResourceSource } from "./resources";
2
+ /** The body of the resource picker: a search box, the app's rows as pictures, previous / next. Mounted afresh each time the dialog opens. */
3
+ type __VLS_Props = {
4
+ source: ResourceSource;
5
+ accept?: string;
6
+ };
7
+ declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
8
+ select: (item: ResourceItem) => any;
9
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
10
+ onSelect?: ((item: ResourceItem) => any) | undefined;
11
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
12
+ export default _default;
@@ -0,0 +1,40 @@
1
+ import { type FieldProps } from "../field";
2
+ import { type ResourceSource } from "./resources";
3
+ /**
4
+ * Formatted text: headings, bold, italic, lists, links, a picture from the media library, optionally a video. The value is an
5
+ * HTML string ("" when empty). The editor is Quill 2, an optional peer dependency loaded when the field first shows
6
+ * (`npm i quill`); an application that never shows this field never loads it.
7
+ *
8
+ * The browser does not sanitise: the HTML is what the editor produced, and the server is the one that cleans it before storing.
9
+ *
10
+ * <RichTextField v-model="form.body" :images="media" video label="Text" />
11
+ */
12
+ type __VLS_Props = FieldProps & {
13
+ modelValue?: string;
14
+ /** The media library for the image button; without it there is no image button. Its `image` URL (else `thumbnail`) is the picture's `src`. */
15
+ images?: ResourceSource;
16
+ /** Shows the video embed button. */
17
+ video?: boolean;
18
+ placeholder?: string;
19
+ };
20
+ declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
21
+ "update:modelValue": (html: string) => any;
22
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
23
+ "onUpdate:modelValue"?: ((html: string) => any) | undefined;
24
+ }>, {
25
+ disabled: boolean;
26
+ placeholder: string;
27
+ name: string;
28
+ label: string;
29
+ video: boolean;
30
+ error: string;
31
+ required: boolean;
32
+ presentation: "control" | "value";
33
+ modelValue: string;
34
+ labelHidden: boolean;
35
+ hint: string;
36
+ editable: boolean;
37
+ lockedReason: string;
38
+ images: ResourceSource;
39
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
40
+ export default _default;
@@ -0,0 +1,5 @@
1
+ import type { InjectionKey, Ref } from "vue";
2
+ /** The language a form in `localeMode: "form"` is showing; its rows (`RepeatField`) follow it instead of showing a switcher of their own. */
3
+ export declare const activeLocaleKey: InjectionKey<Readonly<Ref<string>>>;
4
+ /** How translatable fields show their languages: a segment strip per field (`I18nField`), or one switcher for the whole form. */
5
+ export type LocaleMode = "perField" | "form";
@@ -0,0 +1,34 @@
1
+ /** An id the app's media library gives a resource (a picture, a document). */
2
+ export type ResourceId = number | string;
3
+ /** A row of the app's resource list; the picker needs nothing more. */
4
+ export interface ResourceItem {
5
+ readonly id: ResourceId;
6
+ readonly name?: string;
7
+ }
8
+ /** What the picker asks the app's list for. `accept` is the field's `props.accept` (`"image/*"`). */
9
+ export interface ResourceQuery {
10
+ readonly search: string;
11
+ /** From 1. */
12
+ readonly page: number;
13
+ readonly pageSize: number;
14
+ readonly accept?: string;
15
+ }
16
+ /** The part of a list page the picker reads; a `ListPage<ResourceItem>` fits. */
17
+ export interface ResourcePage {
18
+ readonly rows: readonly ResourceItem[];
19
+ readonly lastPage: number;
20
+ }
21
+ /**
22
+ * Where resources come from: the app's, since only it knows its media library (folders, search, type filter go into
23
+ * `list`). A field holds ids; `thumbnail` turns one into a picture.
24
+ */
25
+ export interface ResourceSource {
26
+ list(query: ResourceQuery, context: {
27
+ signal: AbortSignal;
28
+ }): Promise<ResourcePage>;
29
+ /** A URL with `{id}` in it: `"/media/{id}/thumb"`. */
30
+ thumbnail: string;
31
+ /** The URL of the full picture, for an image placed in rich text. Default: `thumbnail`. */
32
+ image?: string;
33
+ }
34
+ export declare const thumbnailUrl: (source: Pick<ResourceSource, "thumbnail">, id: ResourceId) => string;
@@ -0,0 +1,73 @@
1
+ /**
2
+ * The typed schema `DynamicForm`, `DynamicCollection` and `buildValidator` are generated from: the shape a content
3
+ * type's definition has in the API (snake_case keys, as the server sends them). It is data, not a page description:
4
+ * a list of fields with a kind, nothing about layout.
5
+ */
6
+ /** The kinds a field can have. `image`, `document` and `gallery` hold ids of resources (one, one, several). */
7
+ export type FieldKind = "text" | "textarea" | "rich_text" | "number" | "boolean" | "select" | "multi_select" | "date" | "datetime" | "image" | "document" | "gallery" | "collection";
8
+ /** One test of a `visible_when` rule: the field named, compared with one operator. */
9
+ export type VisibilityCondition = {
10
+ readonly field: string;
11
+ readonly eq: unknown;
12
+ } | {
13
+ readonly field: string;
14
+ readonly ne: unknown;
15
+ } | {
16
+ readonly field: string;
17
+ readonly in: readonly unknown[];
18
+ } | {
19
+ readonly field: string;
20
+ readonly empty: true;
21
+ } | {
22
+ readonly field: string;
23
+ readonly not_empty: true;
24
+ };
25
+ /** When a field shows: every condition of `all` holds, or at least one of `any`. See `evaluateVisibility`. */
26
+ export type VisibilityRule = {
27
+ readonly all: readonly VisibilityCondition[];
28
+ readonly any?: undefined;
29
+ } | {
30
+ readonly any: readonly VisibilityCondition[];
31
+ readonly all?: undefined;
32
+ };
33
+ export interface SelectChoice {
34
+ readonly value: string | number;
35
+ readonly label: string;
36
+ }
37
+ /** A rule of the whitelist a field may carry (the server's list, mirrored); `args` is the rule's parameters (`max`: `[150]`). */
38
+ export interface FieldRule {
39
+ readonly rule: "required" | "min" | "max" | "email" | "url" | "regex";
40
+ readonly args?: readonly unknown[];
41
+ }
42
+ /** What a control shows, never what is valid (that is `validation`). */
43
+ export interface SchemaFieldProps {
44
+ readonly placeholder?: string;
45
+ readonly hint?: string;
46
+ /** A resource field: the kinds the picker lists (`"image/*"`), passed to the app's `list(query)`. */
47
+ readonly accept?: string;
48
+ /** A `textarea`'s rows. */
49
+ readonly rows?: number;
50
+ /** A `rich_text` field: show the video embed button. */
51
+ readonly video?: boolean;
52
+ /** A `collection` field: the label of its add button. */
53
+ readonly addLabel?: string;
54
+ }
55
+ export interface SchemaField {
56
+ readonly key: string;
57
+ readonly kind: FieldKind;
58
+ readonly label: string;
59
+ readonly required?: boolean;
60
+ /** Text kinds only: the value is `Record<locale, string>`. */
61
+ readonly translatable?: boolean;
62
+ readonly props?: SchemaFieldProps;
63
+ /** `select` and `multi_select`. */
64
+ readonly options?: readonly SelectChoice[];
65
+ /** Whitelisted rules besides `required`: `min`/`max` (a text's length, a number's value, a list's size), `email`, `url`, `regex`. */
66
+ readonly validation?: readonly FieldRule[];
67
+ readonly visible_when?: VisibilityRule;
68
+ /** `collection` only: the fields of each row. */
69
+ readonly children?: readonly SchemaField[];
70
+ }
71
+ export interface FormSchema {
72
+ readonly fields: readonly SchemaField[];
73
+ }
@@ -0,0 +1,30 @@
1
+ import type * as Zod from "zod";
2
+ import type { FormValidator } from "../validation";
3
+ import type { FormSchema } from "./schema";
4
+ export interface SchemaValidatorOptions {
5
+ /**
6
+ * The `zod` module (`import * as zod from "zod"`). Passed in, not imported here, so an application that never validates a
7
+ * schema does not need Zod installed: it is an optional peer, and an import in this file would load it with `@wssto2/vue-core/form`.
8
+ */
9
+ zod: typeof Zod;
10
+ /**
11
+ * Texts that replace Zod's. Without them the issues carry Zod's own messages, which `zodErrorMap(application)` words in the
12
+ * application's language (a missing value reads as `validation_errors.required`).
13
+ */
14
+ messages?: Partial<Record<"required" | "email" | "url" | "regex" | "type", string>>;
15
+ /** The language a required translatable text needs. Default: any language written. */
16
+ defaultLocale?: string;
17
+ }
18
+ /**
19
+ * A validator for a schema, with the shape `useForm` takes (`safeParse`), built on Zod 4. It checks what the server will: `required`,
20
+ * the type of each kind, `min` / `max` (a text's length, a number's value, the size of a list or collection), `email`, `url`,
21
+ * `regex`, and the rows of a collection. A field whose `visible_when` does not hold is skipped, and left out of the output, as is a
22
+ * key the schema does not list. The server is authoritative; this only saves the round trip.
23
+ *
24
+ * const validator = buildValidator(schema, { zod });
25
+ * const form = useForm({ defaults, validator });
26
+ *
27
+ * A `regex` argument must be valid for both JavaScript and Go's RE2 (no look-around, no back-references). A rule the schema
28
+ * words wrongly (`max` without a number) throws when the validator is built, not on the user's first keystroke.
29
+ */
30
+ export declare function buildValidator(schema: FormSchema, options: SchemaValidatorOptions): FormValidator<Record<string, unknown>>;
@@ -0,0 +1,12 @@
1
+ import type { VisibilityRule } from "./schema";
2
+ /** Empty means not entered: null, undefined, "", an empty list, and an object (a translated text) with no non-blank entry. `false` and `0` are values. */
3
+ export declare function isEmptyValue(value: unknown): boolean;
4
+ /**
5
+ * Whether a field with this `visible_when` rule shows, given the form's values. A pure function; the server evaluates the same
6
+ * rule (a hidden field is neither required nor validated) and both run `test-data/content/visibility.json`.
7
+ *
8
+ * - no rule, or a rule with no conditions: visible;
9
+ * - `all`: every condition holds; `any`: at least one does;
10
+ * - a malformed condition (unknown operator, `in` without a list) is skipped, and a rule left with none is visible: a broken rule never hides a field for good.
11
+ */
12
+ export declare function evaluateVisibility(rule: VisibilityRule | null | undefined, values: Readonly<Record<string, unknown>>): boolean;
@@ -81,3 +81,16 @@ export type { StepDefinition, StepDialogBinding, StepDraftOptions, StepFlow, Ste
81
81
  export { default as StepForm } from "./StepForm.vue";
82
82
  export { default as StepNavigation } from "./StepNavigation.vue";
83
83
  export { default as StepProgress } from "./StepProgress.vue";
84
+ export { default as DynamicForm } from "./dynamic/DynamicForm.vue";
85
+ export { default as RepeatField } from "./dynamic/RepeatField.vue";
86
+ export { default as ResourceField } from "./dynamic/ResourceField.vue";
87
+ export { default as RichTextField } from "./dynamic/RichTextField.vue";
88
+ export { default as CodeField } from "./CodeField.vue";
89
+ export type { CodeLanguage, CodeMarker } from "./codeEditor";
90
+ export { evaluateVisibility, isEmptyValue } from "./dynamic/visibility";
91
+ export { buildValidator } from "./dynamic/validator";
92
+ export type { SchemaValidatorOptions } from "./dynamic/validator";
93
+ export type { LocaleMode } from "./dynamic/localeMode";
94
+ export { thumbnailUrl } from "./dynamic/resources";
95
+ export type { ResourceId, ResourceItem, ResourcePage, ResourceQuery, ResourceSource } from "./dynamic/resources";
96
+ export type { FieldKind, FieldRule, FormSchema, SchemaField, SelectChoice, VisibilityCondition, VisibilityRule } from "./dynamic/schema";