@wssto2/vue-core 0.17.0 → 0.18.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/CHANGELOG.md +12 -0
- package/README.md +1 -0
- package/dist/collection/DynamicCollection.vue.d.ts +69 -0
- package/dist/collection/definition.d.ts +4 -4
- package/dist/collection/dynamicColumns.d.ts +13 -0
- package/dist/collection/index.d.ts +1 -0
- package/dist/collection/index.js +195 -146
- package/dist/form/dynamic/DynamicForm.vue.d.ts +46 -0
- package/dist/form/dynamic/RepeatField.vue.d.ts +47 -0
- package/dist/form/dynamic/ResourceField.vue.d.ts +38 -0
- package/dist/form/dynamic/ResourcePickerList.vue.d.ts +12 -0
- package/dist/form/dynamic/RichTextField.vue.d.ts +40 -0
- package/dist/form/dynamic/localeMode.d.ts +5 -0
- package/dist/form/dynamic/resources.d.ts +34 -0
- package/dist/form/dynamic/schema.d.ts +73 -0
- package/dist/form/dynamic/validator.d.ts +30 -0
- package/dist/form/dynamic/visibility.d.ts +12 -0
- package/dist/form/index.d.ts +11 -0
- package/dist/form/index.js +1989 -1179
- package/dist/i18n/index.js +12 -12
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/resources-BZLlAtoS.js +62 -0
- package/dist/styles.css +1 -1
- package/package.json +6 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.18.0 — 2026-11-06
|
|
4
|
+
|
|
5
|
+
Forms and lists built from a schema at run time, for content whose fields are data (site-builder's content types). The `Dynamic*` pair generates fields and columns from a typed schema; it is not a page renderer.
|
|
6
|
+
|
|
7
|
+
### New
|
|
8
|
+
- **form:** `DynamicForm` (a form from a `FormSchema`: each field kind mapped to the existing field, `visible_when` hiding, `localeMode: "perField" | "form"`, go-core's 422 field messages), `RepeatField` (repeatable group of fields, reordered with buttons), `ResourceField` (one or several ids picked through `PickerModal` over an app-supplied `list`), `RichTextField` (Quill 2, optional peer `quill`, loaded on first mount; HTML string, no sanitising in the browser), `evaluateVisibility(rule, values)` and `isEmptyValue`, `buildValidator(schema, { zod })`, and the schema types (`FormSchema`, `SchemaField`, `FieldKind`, `VisibilityRule`, `VisibilityCondition`, `ResourceSource`). New texts `core.form.dynamic.*` in the five locales. Recipe: `docs/recipes/content-forms.md`.
|
|
9
|
+
- **collection:** `DynamicCollection`: columns, sort and filters from a schema, on `defineCollection`/`CollectionPage`.
|
|
10
|
+
- **test-data:** `test-data/content/visibility.json`, the `visible_when` cases shared with servers that evaluate the same rule (copy it, do not edit the copy).
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- **collection:** `RowOf`, `SortOf`, `FilterOf` and `ViewOf` were `never` for every definition; they now infer from the definition, and runtime keys (`Row = Record<string, unknown>`) type-check.
|
|
14
|
+
|
|
3
15
|
## 0.17.0 — 2026-11-04
|
|
4
16
|
|
|
5
17
|
What every app built on vue-core wrote again: the phase and to-do components, date values in the date fields' own shape, a lazy locale loader, the author of a list row, "today at 14:35", typed access, two architecture rules as data, and the Playwright and vitest setup. Breaking only in that `Platform.access` is now typed by the registered permissions (see Changed).
|
package/README.md
CHANGED
|
@@ -336,6 +336,7 @@ The library's own texts are under the `core` namespace in English, Croatian, Bos
|
|
|
336
336
|
- [Shell](docs/recipes/shell.md): the default shell, contributions from features, keyboard shortcuts and their help, running inside a frame, a custom shell
|
|
337
337
|
- [App setup](docs/recipes/app-setup.md): config, session, effects, locales and flags, permissions in the UI, error sentences, startup errors
|
|
338
338
|
- [Forms](docs/recipes/forms.md): form state, fields, a record edited in group sheets, a long form, commands
|
|
339
|
+
- [Forms and lists from a schema](docs/recipes/content-forms.md): `DynamicForm`, `RepeatField`, `ResourceField`, `RichTextField`, `buildValidator`, `DynamicCollection`, `visible_when`
|
|
339
340
|
- [Typed routes](docs/recipes/typed-routes.md): generated routes, `client.request`, a list from a route
|
|
340
341
|
- [Sign-in](docs/recipes/sign-in.md): go-core's identity module: the sign-in page, the session wiring, the language, the module's committed types, the dev server
|
|
341
342
|
- [Users and profile](docs/recipes/users.md): the users list and a person's record, "my profile" with the e-mail change by code, opting out, extra sections, refusals
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { RouteLocationRaw } from "vue-router";
|
|
2
|
+
import type { IconName } from "../icon";
|
|
3
|
+
import type { PageAction } from "../page";
|
|
4
|
+
import type { FormSchema } from "../form/dynamic/schema";
|
|
5
|
+
import type { RowAction } from "./columns";
|
|
6
|
+
import type { CollectionLoader, CollectionStateSource } from "./types";
|
|
7
|
+
type Row = Record<string, unknown>;
|
|
8
|
+
/**
|
|
9
|
+
* A list page generated from a typed schema: a column per listable field, sort on the fields the backend can order by, and a
|
|
10
|
+
* filter per select, boolean and date field, all from the same schema. It is `defineCollection` + `useCollection` +
|
|
11
|
+
* `CollectionPage` with `Row = Record<string, unknown>`; the loader is yours (it receives the query with those sort and filter keys).
|
|
12
|
+
*
|
|
13
|
+
* <DynamicCollection id="content.news" :schema="schema" :load="api.entries" title="News" :record-route="(row) => `/news/${row.id}`" thumbnail="/media/{id}/thumb" />
|
|
14
|
+
*
|
|
15
|
+
* Cells: text, number, date and datetime as the standard kinds; boolean as a yes / no badge; select as a badge with the option's label;
|
|
16
|
+
* translated text in the current language; image as a picture from `thumbnail`. The first text field (or `titleField`) is the
|
|
17
|
+
* record's name and links to it. A `#cell-<key>` slot replaces any cell. The schema is read once, when the page is set up: key the
|
|
18
|
+
* component by the type to show another.
|
|
19
|
+
*
|
|
20
|
+
* Filters: a select offers its options, a boolean "yes" / "no" (the values `"true"` and `"false"`), a date the date presets.
|
|
21
|
+
*/
|
|
22
|
+
type __VLS_Props = {
|
|
23
|
+
schema: FormSchema;
|
|
24
|
+
/** The list's identity (saved views and logs use it). */
|
|
25
|
+
id: string;
|
|
26
|
+
load: CollectionLoader<Row, string, string, string>;
|
|
27
|
+
title: string;
|
|
28
|
+
description?: string;
|
|
29
|
+
icon?: IconName;
|
|
30
|
+
actions?: readonly PageAction[];
|
|
31
|
+
/** Where a row's record lives. */
|
|
32
|
+
recordRoute?: (row: Row) => RouteLocationRaw;
|
|
33
|
+
rowActions?: (row: Row) => RowAction[];
|
|
34
|
+
/** Default: the URL's query under `q`. */
|
|
35
|
+
state?: CollectionStateSource;
|
|
36
|
+
/** The field that names the record and links to it. Default: the first text field. */
|
|
37
|
+
titleField?: string;
|
|
38
|
+
/** The columns to show, in order. Default: every listable field of the schema. */
|
|
39
|
+
columns?: readonly string[];
|
|
40
|
+
/** A URL with `{id}` for the pictures of image fields. */
|
|
41
|
+
thumbnail?: string;
|
|
42
|
+
/** The language of translated text. Default: the interface's. */
|
|
43
|
+
locale?: string;
|
|
44
|
+
};
|
|
45
|
+
declare var __VLS_6: string, __VLS_7: {
|
|
46
|
+
[x: string]: unknown;
|
|
47
|
+
};
|
|
48
|
+
type __VLS_Slots = {} & {
|
|
49
|
+
[K in NonNullable<typeof __VLS_6>]?: (props: typeof __VLS_7) => any;
|
|
50
|
+
};
|
|
51
|
+
declare const __VLS_component: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
|
|
52
|
+
icon: IconName;
|
|
53
|
+
state: CollectionStateSource;
|
|
54
|
+
locale: string;
|
|
55
|
+
description: string;
|
|
56
|
+
actions: readonly PageAction[];
|
|
57
|
+
columns: readonly string[];
|
|
58
|
+
thumbnail: string;
|
|
59
|
+
rowActions: (row: Row) => RowAction[];
|
|
60
|
+
recordRoute: (row: Row) => RouteLocationRaw;
|
|
61
|
+
titleField: string;
|
|
62
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
63
|
+
declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
|
|
64
|
+
export default _default;
|
|
65
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
66
|
+
new (): {
|
|
67
|
+
$slots: S;
|
|
68
|
+
};
|
|
69
|
+
};
|
|
@@ -81,7 +81,7 @@ export interface CollectionDefinition<Row, Sort extends string = string, Filter
|
|
|
81
81
|
*/
|
|
82
82
|
export declare function defineCollection<Row, Sort extends string = string, Filter extends string = string, View extends string = string, Meta extends ListMeta = ListMeta>(input: CollectionDefinitionInput<Row, Sort, Filter, View, Meta>): CollectionDefinition<Row, Sort, Filter, View, Meta>;
|
|
83
83
|
/** The row, sort keys, filter keys and views of a definition, for typing code written against it. */
|
|
84
|
-
export type RowOf<D> = D extends CollectionDefinition<infer Row,
|
|
85
|
-
export type SortOf<D> = D extends CollectionDefinition<
|
|
86
|
-
export type FilterOf<D> = D extends CollectionDefinition<
|
|
87
|
-
export type ViewOf<D> = D extends CollectionDefinition<
|
|
84
|
+
export type RowOf<D> = D extends CollectionDefinition<infer Row, infer _S, infer _F, infer _V, infer _M> ? Row : never;
|
|
85
|
+
export type SortOf<D> = D extends CollectionDefinition<infer _R, infer Sort, infer _F, infer _V, infer _M> ? Sort : never;
|
|
86
|
+
export type FilterOf<D> = D extends CollectionDefinition<infer _R, infer _S, infer Filter, infer _V, infer _M> ? Filter : never;
|
|
87
|
+
export type ViewOf<D> = D extends CollectionDefinition<infer _R, infer _S, infer _F, infer View, infer _M> ? View : never;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { FormSchema, SchemaField } from "../form/dynamic/schema";
|
|
2
|
+
import type { FilterDescriptor } from "./filters";
|
|
3
|
+
/** The fields shown as columns: `only` (in its order), else every listable field of the schema. */
|
|
4
|
+
export declare function listedFields(schema: FormSchema, only?: readonly string[]): SchemaField[];
|
|
5
|
+
export declare const isSortable: (field: SchemaField) => boolean;
|
|
6
|
+
export declare const isFilterable: (field: SchemaField) => boolean;
|
|
7
|
+
/** The text of a cell value: a translated text (`{ hr, en }`) in `locale`, else the first language written; anything else as it is. */
|
|
8
|
+
export declare function textOf(value: unknown, locale: string): string;
|
|
9
|
+
/** What a select-like filter offers for a field; a boolean offers yes and no as "true" and "false". */
|
|
10
|
+
export declare function optionsOf(field: SchemaField, labels: {
|
|
11
|
+
yes: string;
|
|
12
|
+
no: string;
|
|
13
|
+
}): FilterDescriptor["options"];
|
|
@@ -28,3 +28,4 @@ export type { CellSlots, CollectionSlots, RowsSlots } from "./slots";
|
|
|
28
28
|
export { createPicker } from "./picker";
|
|
29
29
|
export type { Picker, PickerOptions } from "./picker";
|
|
30
30
|
export { default as PickerModal } from "./PickerModal.vue";
|
|
31
|
+
export { default as DynamicCollection } from "./DynamicCollection.vue";
|
package/dist/collection/index.js
CHANGED
|
@@ -1,192 +1,241 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { a as
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { _ as
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import { _ as
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
1
|
+
import { d as H } from "../http-D6qOS6xs.js";
|
|
2
|
+
import { D as Fe, h as $e, l as Ie, a as je, r as Ne, b as ze } from "../http-D6qOS6xs.js";
|
|
3
|
+
import { u as J, _ as K } from "../CollectionPage.vue_vue_type_script_setup_true_lang-Dr6nFNBB.js";
|
|
4
|
+
import { a as Ve } from "../CollectionPage.vue_vue_type_script_setup_true_lang-Dr6nFNBB.js";
|
|
5
|
+
import { L as Be } from "../state-z1iPF1DT.js";
|
|
6
|
+
import { a as Ge, u as He } from "../neighbors-Dg_LU9ry.js";
|
|
7
|
+
import { r as x } from "../route-goWMxDUN.js";
|
|
8
|
+
import { b as Ke, a as Xe } from "../rowLayout-PIuD7MGo.js";
|
|
9
|
+
import { _ as Qe } from "../DataTable.vue_vue_type_script_setup_true_lang-D372wlxR.js";
|
|
10
|
+
import { defineComponent as z, openBlock as c, createElementBlock as m, Fragment as S, renderList as g, createBlock as M, unref as k, withCtx as C, normalizeClass as O, createVNode as P, createCommentVNode as X, computed as v, useSlots as Z, createSlots as Q, renderSlot as W, normalizeProps as Y, guardReactiveProps as ee, toDisplayString as _ } from "vue";
|
|
11
|
+
import { _ as R } from "../Icon.vue_vue_type_script_lang-BhhsFYwp.js";
|
|
12
|
+
import { _ as te } from "../Tooltip.vue_vue_type_script_setup_true_lang-C1C1qeBj.js";
|
|
13
|
+
import { u as ne } from "../datePresets-DB13HHHd.js";
|
|
14
|
+
import { D as Ye } from "../datePresets-DB13HHHd.js";
|
|
15
|
+
import { _ as tt, h as nt } from "../Highlight.vue_vue_type_script_setup_true_lang-DZGBmwcX.js";
|
|
16
|
+
import { t as oe } from "../resources-BZLlAtoS.js";
|
|
17
|
+
import { _ as st, c as at } from "../resources-BZLlAtoS.js";
|
|
18
|
+
import { useI18n as se } from "vue-i18n";
|
|
19
|
+
function Ae(o, t) {
|
|
20
|
+
const s = o?.authors;
|
|
21
|
+
return t == null || !Array.isArray(s) ? null : s.find((a) => a.id === t) ?? null;
|
|
18
22
|
}
|
|
19
|
-
const
|
|
20
|
-
index:
|
|
21
|
-
store:
|
|
22
|
-
delete:
|
|
23
|
-
},
|
|
24
|
-
function
|
|
23
|
+
const w = {
|
|
24
|
+
index: x("GET", "/v1/saved-views"),
|
|
25
|
+
store: x("POST", "/v1/saved-views"),
|
|
26
|
+
delete: x("DELETE", "/v1/saved-views/:id")
|
|
27
|
+
}, F = ({ id: o, name: t, state: s }) => ({ id: o, name: t, state: s });
|
|
28
|
+
function Te(o) {
|
|
25
29
|
return {
|
|
26
|
-
async list(
|
|
27
|
-
return (await
|
|
30
|
+
async list(t, { signal: s }) {
|
|
31
|
+
return (await o.request(w.index, { list: t }, { signal: s })).data.map(F);
|
|
28
32
|
},
|
|
29
|
-
async save(
|
|
30
|
-
const
|
|
31
|
-
return
|
|
33
|
+
async save(t, s, a) {
|
|
34
|
+
const r = JSON.stringify({ ...a, view: a.view || void 0 });
|
|
35
|
+
return F((await o.request(w.store, { list_key: t, name: s, state: r })).data);
|
|
32
36
|
},
|
|
33
|
-
async remove(
|
|
34
|
-
await
|
|
37
|
+
async remove(t, s) {
|
|
38
|
+
await o.request(w.delete, { id: Number(s) });
|
|
35
39
|
}
|
|
36
40
|
};
|
|
37
41
|
}
|
|
38
|
-
function
|
|
39
|
-
const
|
|
40
|
-
let
|
|
41
|
-
const
|
|
42
|
-
let
|
|
43
|
-
return
|
|
42
|
+
function Le(o = {}) {
|
|
43
|
+
const t = new Map(Object.entries(o).map(([r, n]) => [r, [...n]]));
|
|
44
|
+
let s = 1;
|
|
45
|
+
const a = (r) => {
|
|
46
|
+
let n = t.get(r);
|
|
47
|
+
return n || t.set(r, n = []), n;
|
|
44
48
|
};
|
|
45
49
|
return {
|
|
46
|
-
async list(
|
|
47
|
-
return [...
|
|
50
|
+
async list(r) {
|
|
51
|
+
return [...a(r)];
|
|
48
52
|
},
|
|
49
|
-
async save(
|
|
50
|
-
const
|
|
51
|
-
return
|
|
53
|
+
async save(r, n, f) {
|
|
54
|
+
const u = a(r), y = u.findIndex((h) => h.name === n), b = { id: y === -1 ? `view-${s++}` : u[y].id, name: n, state: f };
|
|
55
|
+
return y === -1 ? u.push(b) : u[y] = b, b;
|
|
52
56
|
},
|
|
53
|
-
async remove(
|
|
54
|
-
const
|
|
55
|
-
|
|
57
|
+
async remove(r, n) {
|
|
58
|
+
const f = a(r), u = f.findIndex((y) => y.id === n);
|
|
59
|
+
u !== -1 && f.splice(u, 1);
|
|
56
60
|
}
|
|
57
61
|
};
|
|
58
62
|
}
|
|
59
|
-
const
|
|
63
|
+
const ae = {
|
|
60
64
|
key: 0,
|
|
61
65
|
class: "flex items-center justify-center gap-1",
|
|
62
66
|
"data-test": "row-actions"
|
|
63
|
-
},
|
|
67
|
+
}, re = ["href", "target", "rel", "aria-label", "data-action"], ie = ["aria-label", "data-action", "disabled", "onClick"], $ = "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-border-focus", De = /* @__PURE__ */ z({
|
|
64
68
|
__name: "RowActions",
|
|
65
69
|
props: {
|
|
66
70
|
actions: {},
|
|
67
71
|
disabled: { type: Boolean, default: !1 }
|
|
68
72
|
},
|
|
69
|
-
setup(
|
|
70
|
-
const
|
|
71
|
-
return (
|
|
72
|
-
(c(!0),
|
|
73
|
-
key:
|
|
74
|
-
text:
|
|
73
|
+
setup(o) {
|
|
74
|
+
const t = o, s = (a) => a.tone === "critical" ? "text-status-danger-content hover:bg-status-danger-surface" : "text-content-link hover:bg-tint-soft";
|
|
75
|
+
return (a, r) => t.actions.length > 0 ? (c(), m("div", ae, [
|
|
76
|
+
(c(!0), m(S, null, g(t.actions, (n) => (c(), M(k(te), {
|
|
77
|
+
key: n.key,
|
|
78
|
+
text: n.label,
|
|
75
79
|
side: "left"
|
|
76
80
|
}, {
|
|
77
|
-
default:
|
|
78
|
-
|
|
81
|
+
default: C(() => [
|
|
82
|
+
n.href && !t.disabled ? (c(), m("a", {
|
|
79
83
|
key: 0,
|
|
80
|
-
href:
|
|
81
|
-
target:
|
|
82
|
-
rel:
|
|
83
|
-
"aria-label":
|
|
84
|
-
"data-action":
|
|
85
|
-
class:
|
|
84
|
+
href: n.href,
|
|
85
|
+
target: n.external ? "_blank" : void 0,
|
|
86
|
+
rel: n.external ? "noopener" : void 0,
|
|
87
|
+
"aria-label": n.label,
|
|
88
|
+
"data-action": n.key,
|
|
89
|
+
class: O(["inline-flex rounded-md p-1.5 transition-colors", [s(n), $]])
|
|
86
90
|
}, [
|
|
87
|
-
|
|
88
|
-
name:
|
|
91
|
+
P(k(R), {
|
|
92
|
+
name: n.icon,
|
|
89
93
|
size: 18
|
|
90
94
|
}, null, 8, ["name"])
|
|
91
|
-
], 10,
|
|
95
|
+
], 10, re)) : (c(), m("button", {
|
|
92
96
|
key: 1,
|
|
93
97
|
type: "button",
|
|
94
|
-
"aria-label":
|
|
95
|
-
"data-action":
|
|
96
|
-
disabled:
|
|
97
|
-
class:
|
|
98
|
-
onClick: (
|
|
98
|
+
"aria-label": n.label,
|
|
99
|
+
"data-action": n.key,
|
|
100
|
+
disabled: t.disabled,
|
|
101
|
+
class: O(["inline-flex cursor-pointer rounded-md p-1.5 transition-colors disabled:pointer-events-none disabled:opacity-40", [s(n), $]]),
|
|
102
|
+
onClick: (f) => n.onSelect?.()
|
|
99
103
|
}, [
|
|
100
|
-
|
|
101
|
-
name:
|
|
104
|
+
P(k(R), {
|
|
105
|
+
name: n.icon,
|
|
102
106
|
size: 18
|
|
103
107
|
}, null, 8, ["name"])
|
|
104
|
-
], 10,
|
|
108
|
+
], 10, ie))
|
|
105
109
|
]),
|
|
106
110
|
_: 2
|
|
107
111
|
}, 1032, ["text"]))), 128))
|
|
108
|
-
])) :
|
|
112
|
+
])) : X("", !0);
|
|
109
113
|
}
|
|
110
|
-
});
|
|
111
|
-
function
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
114
|
+
}), le = ["text", "number", "boolean", "select", "multi_select", "date", "datetime", "image", "document", "gallery"], ce = ["text", "number", "boolean", "select", "date", "datetime"], de = ["select", "boolean", "date", "datetime"];
|
|
115
|
+
function ue(o, t) {
|
|
116
|
+
return t ? t.flatMap((s) => o.fields.filter((a) => a.key === s)) : o.fields.filter((s) => le.includes(s.kind));
|
|
117
|
+
}
|
|
118
|
+
const I = (o) => ce.includes(o.kind) && !o.translatable, j = (o) => de.includes(o.kind) && !o.translatable;
|
|
119
|
+
function N(o, t) {
|
|
120
|
+
if (o == null) return "";
|
|
121
|
+
if (typeof o == "object" && !Array.isArray(o)) {
|
|
122
|
+
const s = o, a = s[t];
|
|
123
|
+
return typeof a == "string" && a !== "" ? a : Object.values(s).find((r) => typeof r == "string" && r !== "") ?? "";
|
|
117
124
|
}
|
|
118
|
-
return
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
e = o, a.value = { open: !0, options: s ?? {} };
|
|
123
|
-
});
|
|
124
|
-
},
|
|
125
|
-
select: (s) => i(s),
|
|
126
|
-
cancel: () => i(null)
|
|
127
|
-
};
|
|
125
|
+
return String(o);
|
|
126
|
+
}
|
|
127
|
+
function me(o, t) {
|
|
128
|
+
return o.kind === "boolean" ? [{ value: "true", label: t.yes }, { value: "false", label: t.no }] : (o.options ?? []).map((s) => ({ value: s.value, label: s.label }));
|
|
128
129
|
}
|
|
129
|
-
const
|
|
130
|
-
__name: "
|
|
130
|
+
const ye = { key: 0 }, pe = ["src"], fe = { key: 2 }, ke = { key: 3 }, be = { key: 4 }, Oe = /* @__PURE__ */ z({
|
|
131
|
+
__name: "DynamicCollection",
|
|
131
132
|
props: {
|
|
132
|
-
|
|
133
|
-
|
|
133
|
+
schema: {},
|
|
134
|
+
id: {},
|
|
135
|
+
load: {},
|
|
136
|
+
title: {},
|
|
134
137
|
description: { default: void 0 },
|
|
135
|
-
|
|
138
|
+
icon: { default: void 0 },
|
|
139
|
+
actions: { default: () => [] },
|
|
140
|
+
recordRoute: { type: Function, default: void 0 },
|
|
141
|
+
rowActions: { type: Function, default: void 0 },
|
|
142
|
+
state: { default: () => ({ kind: "url", key: "q" }) },
|
|
143
|
+
titleField: { default: void 0 },
|
|
144
|
+
columns: { default: void 0 },
|
|
145
|
+
thumbnail: { default: void 0 },
|
|
146
|
+
locale: { default: void 0 }
|
|
136
147
|
},
|
|
137
|
-
setup(
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
148
|
+
setup(o) {
|
|
149
|
+
const t = o, { t: s, locale: a } = se(), r = v(() => t.locale ?? a.value), n = ue(t.schema, t.columns), f = t.titleField ?? n.find((e) => e.kind === "text")?.key, u = v(() => ({ yes: s("core.form.yes"), no: s("core.form.no") })), y = H({
|
|
150
|
+
id: t.id,
|
|
151
|
+
stateVersion: 1,
|
|
152
|
+
load: t.load,
|
|
153
|
+
key: (e) => e.id,
|
|
154
|
+
query: { sorts: n.filter(I).map((e) => e.key), filters: n.filter(j).map((e) => e.key) }
|
|
155
|
+
}), b = new Map(n.filter((e) => e.kind === "date" || e.kind === "datetime").map((e) => [e.key, ne(e.key, e.label)])), h = v(() => n.filter(j).map((e) => b.get(e.key)?.value ?? { key: e.key, label: e.label, type: "select", options: me(e, u.value) })), E = (e, i) => e.options?.find((l) => l.value === i)?.label ?? String(i ?? ""), A = v(() => n.map((e) => {
|
|
156
|
+
const i = { key: e.key, label: e.label, ...I(e) ? { sort: e.key } : {} };
|
|
157
|
+
if (e.key === f) return { ...i, kind: "identity", title: (l) => N(l[e.key], r.value) };
|
|
158
|
+
switch (e.kind) {
|
|
159
|
+
case "number":
|
|
160
|
+
return { ...i, kind: "number" };
|
|
161
|
+
case "date":
|
|
162
|
+
return { ...i, kind: "timestamp", precision: "date" };
|
|
163
|
+
case "datetime":
|
|
164
|
+
return { ...i, kind: "timestamp", precision: "dateTime" };
|
|
165
|
+
case "boolean":
|
|
166
|
+
return { ...i, kind: "badge", tone: (l) => l[e.key] === !0 ? "positive" : "neutral", text: (l) => l[e.key] === !0 ? u.value.yes : u.value.no };
|
|
167
|
+
case "select":
|
|
168
|
+
return { ...i, kind: "badge", tone: () => "neutral", text: (l) => E(e, l[e.key]) };
|
|
169
|
+
case "text":
|
|
170
|
+
return e.translatable ? { ...i, kind: "custom" } : { ...i, kind: "text" };
|
|
171
|
+
default:
|
|
172
|
+
return { ...i, kind: "custom" };
|
|
173
|
+
}
|
|
174
|
+
})), V = J(y, {
|
|
175
|
+
columns: A,
|
|
176
|
+
filters: h,
|
|
177
|
+
state: t.state,
|
|
178
|
+
...t.recordRoute ? { recordRoute: t.recordRoute } : {}
|
|
179
|
+
}), T = (e) => `cell-${e.replace(/\./g, "_")}`, L = Z(), q = v(() => A.value.filter((e) => e.kind === "custom" && !(T(e.key) in L))), B = () => Object.keys(L), U = (e) => n.find((i) => i.key === e), D = (e) => Array.isArray(e) ? e : [];
|
|
180
|
+
return (e, i) => (c(), M(K, {
|
|
181
|
+
collection: k(V),
|
|
182
|
+
title: t.title,
|
|
183
|
+
description: t.description,
|
|
184
|
+
icon: t.icon,
|
|
185
|
+
actions: t.actions,
|
|
186
|
+
"row-actions": t.rowActions
|
|
187
|
+
}, Q({ _: 2 }, [
|
|
188
|
+
g(B(), (l) => ({
|
|
189
|
+
name: l,
|
|
190
|
+
fn: C((p) => [
|
|
191
|
+
W(e.$slots, l, Y(ee(p ?? {})))
|
|
192
|
+
])
|
|
193
|
+
})),
|
|
194
|
+
g(q.value, (l) => ({
|
|
195
|
+
name: T(l.key),
|
|
196
|
+
fn: C(({ item: p }) => [
|
|
197
|
+
(c(!0), m(S, null, g([U(l.key)], (d) => (c(), m(S, {
|
|
198
|
+
key: d.key
|
|
199
|
+
}, [
|
|
200
|
+
d.kind === "text" ? (c(), m("span", ye, _(k(N)(p[d.key], r.value)), 1)) : d.kind === "image" && t.thumbnail && p[d.key] != null ? (c(), m("img", {
|
|
201
|
+
key: 1,
|
|
202
|
+
src: k(oe)({ thumbnail: t.thumbnail }, p[d.key]),
|
|
203
|
+
alt: "",
|
|
204
|
+
class: "size-10 rounded object-cover",
|
|
205
|
+
loading: "lazy"
|
|
206
|
+
}, null, 8, pe)) : d.kind === "gallery" ? (c(), m("span", fe, _(D(p[d.key]).length || ""), 1)) : d.kind === "multi_select" ? (c(), m("span", ke, _(D(p[d.key]).map((G) => E(d, G)).join(", ")), 1)) : (c(), m("span", be, _(p[d.key] ?? ""), 1))
|
|
207
|
+
], 64))), 128))
|
|
208
|
+
])
|
|
209
|
+
}))
|
|
210
|
+
]), 1032, ["collection", "title", "description", "icon", "actions", "row-actions"]));
|
|
163
211
|
}
|
|
164
212
|
});
|
|
165
213
|
export {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
214
|
+
Ke as CollectionCell,
|
|
215
|
+
K as CollectionPage,
|
|
216
|
+
Ve as CollectionTable,
|
|
217
|
+
Ye as DATE_PRESETS,
|
|
218
|
+
Fe as DEFAULT_PAGE_SIZES,
|
|
219
|
+
Qe as DataTable,
|
|
220
|
+
Oe as DynamicCollection,
|
|
221
|
+
tt as Highlight,
|
|
222
|
+
Be as LIST_CONTEXT_PARAM,
|
|
223
|
+
st as PickerModal,
|
|
224
|
+
Xe as RecordIdentity,
|
|
225
|
+
De as RowActions,
|
|
226
|
+
Ae as authorOf,
|
|
227
|
+
Le as createMemorySavedViews,
|
|
228
|
+
at as createPicker,
|
|
229
|
+
H as defineCollection,
|
|
230
|
+
nt as highlightParts,
|
|
231
|
+
$e as httpList,
|
|
232
|
+
Ie as listLoader,
|
|
233
|
+
je as listParams,
|
|
234
|
+
Ne as readListPage,
|
|
235
|
+
ze as routeListLoader,
|
|
236
|
+
Te as serverSavedViews,
|
|
237
|
+
J as useCollection,
|
|
238
|
+
Ge as useCollectionBack,
|
|
239
|
+
He as useCollectionNeighbors,
|
|
240
|
+
ne as useDatePresetFilter
|
|
192
241
|
};
|
|
@@ -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;
|