@protobase/schema 0.12.9 → 0.12.11
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/address-errors.d.ts +2 -1
- package/dist/address-errors.js +3 -1
- package/dist/address-template.d.ts +1 -1
- package/dist/address-template.js +6 -1
- package/dist/app-address.js +13 -5
- package/dist/index.d.ts +2 -2
- package/dist/model.d.ts +16 -2
- package/dist/view-displays.d.ts +8 -4
- package/dist/view-displays.js +11 -5
- package/dist/view-field.d.ts +9 -1
- package/dist/view-field.js +23 -0
- package/dist/view.js +3 -0
- package/package.json +1 -1
package/dist/address-errors.d.ts
CHANGED
|
@@ -10,7 +10,8 @@ export type AddressCheck = {
|
|
|
10
10
|
};
|
|
11
11
|
/**
|
|
12
12
|
* What is wrong with a link's address against these models: an unknown resource or page, a `{field}` the record it sits
|
|
13
|
-
* on does not have,
|
|
13
|
+
* on does not have, a filter or order naming fields the resource does not have, or create values for fields it does not
|
|
14
|
+
* have. Empty when it can be followed.
|
|
14
15
|
*/
|
|
15
16
|
export declare const addressErrors: (to: AppAddress, { models, pages, source }: AddressCheck) => string[];
|
|
16
17
|
//#endregion
|
package/dist/address-errors.js
CHANGED
|
@@ -31,7 +31,8 @@ const orderErrors = (model, orderBy) => {
|
|
|
31
31
|
};
|
|
32
32
|
/**
|
|
33
33
|
* What is wrong with a link's address against these models: an unknown resource or page, a `{field}` the record it sits
|
|
34
|
-
* on does not have,
|
|
34
|
+
* on does not have, a filter or order naming fields the resource does not have, or create values for fields it does not
|
|
35
|
+
* have. Empty when it can be followed.
|
|
35
36
|
*/
|
|
36
37
|
const addressErrors = (to, { models, pages, source }) => {
|
|
37
38
|
const placeholders = addressPlaceholders(to).flatMap((name) => {
|
|
@@ -45,6 +46,7 @@ const addressErrors = (to, { models, pages, source }) => {
|
|
|
45
46
|
case "create": {
|
|
46
47
|
const target = Object.hasOwn(models, to.resource) ? models[to.resource] : void 0;
|
|
47
48
|
if (!target) return [`unknown resource "${to.resource}"`];
|
|
49
|
+
if (to.kind === "create") return [...placeholders, ...Object.keys(to.values ?? {}).filter((name) => !Object.hasOwn(target.fields, name)).map((name) => `values: unknown field "${name}" on ${to.resource}`)];
|
|
48
50
|
if (to.kind !== "list") return placeholders;
|
|
49
51
|
return [
|
|
50
52
|
...placeholders,
|
|
@@ -4,7 +4,7 @@ import { AppAddress } from "./model.js";
|
|
|
4
4
|
export declare const addressPlaceholders: (to: AppAddress) => string[];
|
|
5
5
|
/** The fields an href takes from the record, from its `{field}` placeholders. */
|
|
6
6
|
export declare const hrefPlaceholders: (href: string) => string[];
|
|
7
|
-
/** `to` with every `{field}` in its key, filter
|
|
7
|
+
/** `to` with every `{field}` in its key, filter, order or values replaced by the record's value; a missing value becomes empty. */
|
|
8
8
|
export declare const fillAddress: (to: AppAddress, record?: Record<string, unknown>) => AppAddress;
|
|
9
9
|
/**
|
|
10
10
|
* Whether an href leaves the app: it starts with a scheme (`https:`, `mailto:`) or `//`. A path inside the app is an
|
package/dist/address-template.js
CHANGED
|
@@ -4,6 +4,7 @@ const names = (text) => [...text.matchAll(placeholder)].map((match) => match[1])
|
|
|
4
4
|
const templated = (to) => {
|
|
5
5
|
if (to.kind === "record") return (Array.isArray(to.key) ? to.key : [to.key]).filter((part) => typeof part === "string");
|
|
6
6
|
if (to.kind === "list") return [to.filter, to.orderBy].filter((part) => part !== void 0);
|
|
7
|
+
if (to.kind === "create") return Object.values(to.values ?? {});
|
|
7
8
|
return [];
|
|
8
9
|
};
|
|
9
10
|
/** The fields whose values a link's address takes from the record, from its `{field}` placeholders. */
|
|
@@ -11,7 +12,7 @@ const addressPlaceholders = (to) => [...new Set(templated(to).flatMap(names))];
|
|
|
11
12
|
/** The fields an href takes from the record, from its `{field}` placeholders. */
|
|
12
13
|
const hrefPlaceholders = (href) => [...new Set(names(href))];
|
|
13
14
|
const fill = (text, record) => text.replace(placeholder, (_, name) => record[name] === null || record[name] === void 0 ? "" : String(record[name]));
|
|
14
|
-
/** `to` with every `{field}` in its key, filter
|
|
15
|
+
/** `to` with every `{field}` in its key, filter, order or values replaced by the record's value; a missing value becomes empty. */
|
|
15
16
|
const fillAddress = (to, record = {}) => {
|
|
16
17
|
if (to.kind === "record") {
|
|
17
18
|
const part = (value) => typeof value === "string" ? fill(value, record) : value;
|
|
@@ -20,6 +21,10 @@ const fillAddress = (to, record = {}) => {
|
|
|
20
21
|
key: Array.isArray(to.key) ? to.key.map(part) : part(to.key)
|
|
21
22
|
};
|
|
22
23
|
}
|
|
24
|
+
if (to.kind === "create") return to.values ? {
|
|
25
|
+
...to,
|
|
26
|
+
values: Object.fromEntries(Object.entries(to.values).map(([name, value]) => [name, fill(value, record)]))
|
|
27
|
+
} : to;
|
|
23
28
|
if (to.kind !== "list") return to;
|
|
24
29
|
return {
|
|
25
30
|
...to,
|
package/dist/app-address.js
CHANGED
|
@@ -35,7 +35,7 @@ const listQuery = (address) => {
|
|
|
35
35
|
const appPath = (address, basePath = "") => {
|
|
36
36
|
switch (address.kind) {
|
|
37
37
|
case "page": return `${basePath}/${encodeURIComponent(address.page)}`;
|
|
38
|
-
case "create": return `${basePath}/${encodeURIComponent(address.resource)}/${addressWords.create}`;
|
|
38
|
+
case "create": return `${basePath}/${encodeURIComponent(address.resource)}/${addressWords.create}${valuesQuery(address.values)}`;
|
|
39
39
|
case "record": return `${basePath}/${encodeURIComponent(address.resource)}/${encodeURIComponent(encodeKey(address.key))}`;
|
|
40
40
|
case "list": return `${basePath}/${encodeURIComponent(address.resource)}${listQuery(address)}`;
|
|
41
41
|
}
|
|
@@ -48,6 +48,10 @@ const decodeSegments = (pathname) => {
|
|
|
48
48
|
throw error;
|
|
49
49
|
}
|
|
50
50
|
};
|
|
51
|
+
const valuesQuery = (values = {}) => {
|
|
52
|
+
const text = new URLSearchParams(Object.entries(values).filter(([, value]) => value !== "")).toString();
|
|
53
|
+
return text ? `?${text}` : "";
|
|
54
|
+
};
|
|
51
55
|
const filterLayout = (value) => value === "bar" || value === "panel" ? value : void 0;
|
|
52
56
|
/**
|
|
53
57
|
* The address `pathAndQuery` (below the base path) shows, or `undefined` for anything else: the root, the app's own
|
|
@@ -63,10 +67,14 @@ const readAppPath = (pathAndQuery, pages) => {
|
|
|
63
67
|
kind: "page",
|
|
64
68
|
page: first
|
|
65
69
|
} : void 0;
|
|
66
|
-
if (second === addressWords.create)
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
+
if (second === addressWords.create) {
|
|
71
|
+
const values = Object.fromEntries([...url.searchParams].filter(([, value]) => value !== ""));
|
|
72
|
+
return {
|
|
73
|
+
kind: "create",
|
|
74
|
+
resource: first,
|
|
75
|
+
...Object.keys(values).length > 0 && { values }
|
|
76
|
+
};
|
|
77
|
+
}
|
|
70
78
|
if (second !== void 0) return {
|
|
71
79
|
kind: "record",
|
|
72
80
|
resource: first,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ActionModel, ActionRun, AppAddress, BottomBarItemModel, BottomBarModel, ChartGranularity, ChartModel, ChartRange, CheckedFilter, CompareOp, CustomSectionModel, DatePreset, DurationLiteral, DurationUnit, EventModel, EventOperation, FieldFormat, FieldModel, FieldPath, FieldType, FieldViewModel, FieldWidgetModel, FileFieldModel, FilterError, FilterErrorCode, FilterExpr, FilterLayout, FilterOf, FilterValue, FilterWidgetModel, JsonValue, KeyValue, LayoutItemModel, ListDisplayModel, ListModel, ListQuery, Literal, NavModel, NavRecentModel, NowValue, OrderByItem, RelatedModel, ResourceModel, SaveFeedback, SearchMatch, SearchResultModel, SortSpec, Span, StatusTone, UserMenuItemModel, UserMenuModel, ViewModel } from "./model.js";
|
|
1
|
+
import { ActionModel, ActionRun, AppAddress, BadgeShape, BottomBarItemModel, BottomBarModel, ChartGranularity, ChartModel, ChartRange, CheckedFilter, CompareOp, CustomSectionModel, DatePreset, DurationLiteral, DurationUnit, EventModel, EventOperation, FieldFormat, FieldModel, FieldPath, FieldType, FieldViewModel, FieldWidgetModel, FileFieldModel, FilterError, FilterErrorCode, FilterExpr, FilterLayout, FilterOf, FilterValue, FilterWidgetModel, JsonValue, KeyValue, LayoutItemModel, ListDisplayModel, ListModel, ListQuery, Literal, NavModel, NavRecentModel, NowValue, OrderByItem, RelatedModel, ResourceModel, SaveFeedback, SearchMatch, SearchResultModel, SortSpec, Span, StatusTone, UserMenuItemModel, UserMenuModel, ViewModel } from "./model.js";
|
|
2
2
|
import { AccessAction, AccessContext, AccessFn, AccessResult, AccessUser, FieldAccess, FieldRule } from "./access/types.js";
|
|
3
3
|
import { FileProcessor, FileProcessorInput, checkedAccept, checkedProvider, parseSize } from "./files/file-options.js";
|
|
4
4
|
import { Field } from "./field.js";
|
|
@@ -44,4 +44,4 @@ import "./access/index.js";
|
|
|
44
44
|
import { AssistantAction, AssistantActionRequest, AssistantCardPart, AssistantDiff, AssistantEvent, AssistantMessage, AssistantMessageRequest, AssistantPart, AssistantState, AssistantStep, AssistantStepState, AssistantTablePart, AssistantTextPart, AssistantTone, AssistantWidgetPart } from "./assistant/protocol.js";
|
|
45
45
|
import { applyAssistantEvent, emptyAssistantState } from "./assistant/apply-event.js";
|
|
46
46
|
import { readEventStream } from "./assistant/event-stream.js";
|
|
47
|
-
export { type AccessAction, type AccessContext, type AccessFn, type AccessResult, type AccessSource, type AccessUser, type ActionModel, type ActionRun, type AddressCheck, type AppAddress, type AssistantAction, type AssistantActionRequest, type AssistantCardPart, type AssistantDiff, type AssistantEvent, type AssistantMessage, type AssistantMessageRequest, type AssistantPart, type AssistantState, type AssistantStep, type AssistantStepState, type AssistantTablePart, type AssistantTextPart, type AssistantTone, type AssistantWidgetPart, type BottomBarItemModel, type BottomBarModel, type BottomBarNames, type BottomBarSource, type Capability, type CapabilityAction, type CapabilityLimit, type CapabilityScope, type ChartGranularity, type ChartModel, type ChartRange, type CheckResult, type CheckedFilter, type CompareOp, type ComponentRef, type CustomSectionModel, type DatePreset, type DurationLiteral, type DurationUnit, type EvaluateOptions, type EventActor, type EventContext, type EventHelpers, type EventMatch, type EventModel, type EventOperation, type EventSpec, type Field, type FieldAccess, type FieldFormat, type FieldModel, type FieldPath, type FieldRefValue, type FieldRule, type FieldType, type FieldViewModel, type FieldWidgetModel, type FileField, type FileFieldModel, type FileProcessor, type FileProcessorInput, type FilterError, type FilterErrorCode, type FilterExpr, type FilterLayout, type FilterLimits, type FilterOf, type FilterParse, type FilterValue, type FilterWidgetModel, type InferRecord, type JsonValue, type KeyType, type KeyValue, type LayoutItemModel, type ListDisplayModel, type ListModel, type ListQuery, type Literal, type NavModel, type NavRecentModel, type NowValue, type OrderByCheck, type OrderByItem, type OrderByParse, type PluginHost, type PluginRequirement, type PropShapes, type RecordKey, type RefShape, type RelatedModel, type ResolveOptions, type ResolvedAccess, type ResourceModel, type RoleEntry, type RoleLimit, type Roles, type RolesOptions, type RowFilterOperation, type Rule, type SaveFeedback, type SearchMatch, type SearchResultModel, type SearchTerm, type SortSpec, type Span, type StatusTone, type UserMenuItemModel, type UserMenuModel, type UserMenuSource, type ViewModel, type ViewProps, accessWarnings, addressErrors, addressPlaceholders, addressWords, anonymousRole, appHrefError, appPath, applyAssistantEvent, bottomBar, bottomBarErrors, bottomBarFor, checkEventFilter, checkFilter, checkOrderBy, checkRecord, checkedAccept, checkedProvider, createWhere, decodeKey, defaultRecentLimit, defineRoles, displayFields, emptyAssistantState, encodeKey, enumLabel, evaluateFilter, f, fieldRef, fieldRefsIn, fieldsUsedBy, fillAddress, hrefPlaceholders, inRange, isFieldRef, keyTypes, layout as l, leavesApp, markFieldRefs, matchedSearchField, matchesSearch, maxBottomBarItems, navRecentErrors, parseFilter, parseOrderBy, parseSize, pickView, pluginProblem, printFilter, printOrderBy, pruneByShape, readAppPath, readEventStream, redactSecrets, resolveAccess, resource, restrictModel, rule, searchTerms, shapes, statusTones, tableDisplay, termMatches, unmarkFieldRefs, userMenu, view, webhookResources, where };
|
|
47
|
+
export { type AccessAction, type AccessContext, type AccessFn, type AccessResult, type AccessSource, type AccessUser, type ActionModel, type ActionRun, type AddressCheck, type AppAddress, type AssistantAction, type AssistantActionRequest, type AssistantCardPart, type AssistantDiff, type AssistantEvent, type AssistantMessage, type AssistantMessageRequest, type AssistantPart, type AssistantState, type AssistantStep, type AssistantStepState, type AssistantTablePart, type AssistantTextPart, type AssistantTone, type AssistantWidgetPart, type BadgeShape, type BottomBarItemModel, type BottomBarModel, type BottomBarNames, type BottomBarSource, type Capability, type CapabilityAction, type CapabilityLimit, type CapabilityScope, type ChartGranularity, type ChartModel, type ChartRange, type CheckResult, type CheckedFilter, type CompareOp, type ComponentRef, type CustomSectionModel, type DatePreset, type DurationLiteral, type DurationUnit, type EvaluateOptions, type EventActor, type EventContext, type EventHelpers, type EventMatch, type EventModel, type EventOperation, type EventSpec, type Field, type FieldAccess, type FieldFormat, type FieldModel, type FieldPath, type FieldRefValue, type FieldRule, type FieldType, type FieldViewModel, type FieldWidgetModel, type FileField, type FileFieldModel, type FileProcessor, type FileProcessorInput, type FilterError, type FilterErrorCode, type FilterExpr, type FilterLayout, type FilterLimits, type FilterOf, type FilterParse, type FilterValue, type FilterWidgetModel, type InferRecord, type JsonValue, type KeyType, type KeyValue, type LayoutItemModel, type ListDisplayModel, type ListModel, type ListQuery, type Literal, type NavModel, type NavRecentModel, type NowValue, type OrderByCheck, type OrderByItem, type OrderByParse, type PluginHost, type PluginRequirement, type PropShapes, type RecordKey, type RefShape, type RelatedModel, type ResolveOptions, type ResolvedAccess, type ResourceModel, type RoleEntry, type RoleLimit, type Roles, type RolesOptions, type RowFilterOperation, type Rule, type SaveFeedback, type SearchMatch, type SearchResultModel, type SearchTerm, type SortSpec, type Span, type StatusTone, type UserMenuItemModel, type UserMenuModel, type UserMenuSource, type ViewModel, type ViewProps, accessWarnings, addressErrors, addressPlaceholders, addressWords, anonymousRole, appHrefError, appPath, applyAssistantEvent, bottomBar, bottomBarErrors, bottomBarFor, checkEventFilter, checkFilter, checkOrderBy, checkRecord, checkedAccept, checkedProvider, createWhere, decodeKey, defaultRecentLimit, defineRoles, displayFields, emptyAssistantState, encodeKey, enumLabel, evaluateFilter, f, fieldRef, fieldRefsIn, fieldsUsedBy, fillAddress, hrefPlaceholders, inRange, isFieldRef, keyTypes, layout as l, leavesApp, markFieldRefs, matchedSearchField, matchesSearch, maxBottomBarItems, navRecentErrors, parseFilter, parseOrderBy, parseSize, pickView, pluginProblem, printFilter, printOrderBy, pruneByShape, readAppPath, readEventStream, redactSecrets, resolveAccess, resource, restrictModel, rule, searchTerms, shapes, statusTones, tableDisplay, termMatches, unmarkFieldRefs, userMenu, view, webhookResources, where };
|
package/dist/model.d.ts
CHANGED
|
@@ -87,7 +87,8 @@ export type ListQuery = {
|
|
|
87
87
|
export type FilterLayout = 'bar' | 'panel';
|
|
88
88
|
/**
|
|
89
89
|
* A place in the app: a resource's list, one of its records, its create form, or a composed page. A record's `key` is
|
|
90
|
-
* its one value or its parts, as the record API takes them; `readAppPath` gives it back as the API's text form.
|
|
90
|
+
* its one value or its parts, as the record API takes them; `readAppPath` gives it back as the API's text form. A create
|
|
91
|
+
* form's `values` fill in fields of the new record by name, as text (`{ listing_id: '42' }`).
|
|
91
92
|
*/
|
|
92
93
|
export type AppAddress = {
|
|
93
94
|
kind: 'list';
|
|
@@ -103,6 +104,7 @@ export type AppAddress = {
|
|
|
103
104
|
} | {
|
|
104
105
|
kind: 'create';
|
|
105
106
|
resource: string;
|
|
107
|
+
values?: Readonly<Record<string, string>>;
|
|
106
108
|
} | {
|
|
107
109
|
kind: 'page';
|
|
108
110
|
page: string;
|
|
@@ -113,15 +115,24 @@ export type FieldViewModel = {
|
|
|
113
115
|
help?: string;
|
|
114
116
|
format?: FieldFormat;
|
|
115
117
|
prefix?: string;
|
|
118
|
+
/** Text after a number wherever it is shown, such as a unit: `m²` shows `120 m²`. */
|
|
119
|
+
suffix?: string;
|
|
116
120
|
decimals?: number;
|
|
121
|
+
/** An icon before the value on a card's meta row, by the names actions take (`ruler`); an unknown name shows none. */
|
|
122
|
+
icon?: string;
|
|
117
123
|
/** What an enum value reads as wherever it is shown; a value without one reads as itself in words (`enumLabel`). */
|
|
118
124
|
valueLabels?: Record<string, string>;
|
|
125
|
+
/** A hex colour per enum value that fills its badge, such as `{ a: '#00a651' }`; a value without one keeps its tone. */
|
|
126
|
+
valueColors?: Record<string, string>;
|
|
127
|
+
/** `arrow` draws the field's badges as arrows pointing right, as on a rating scale such as the EU energy label. */
|
|
128
|
+
badgeShape?: BadgeShape;
|
|
119
129
|
/**
|
|
120
130
|
* The field type a plugin registers (`definePlugin({ fields })`) that shows and edits this field, with its props;
|
|
121
131
|
* without that plugin, or for a storage type it does not show, the field shows as it would without it.
|
|
122
132
|
*/
|
|
123
133
|
widget?: FieldWidgetModel;
|
|
124
134
|
};
|
|
135
|
+
export type BadgeShape = 'arrow';
|
|
125
136
|
/** A field type by the name a plugin registers it under, and the plain data it is given. */
|
|
126
137
|
export type FieldWidgetModel = {
|
|
127
138
|
name: string;
|
|
@@ -129,17 +140,20 @@ export type FieldWidgetModel = {
|
|
|
129
140
|
};
|
|
130
141
|
/**
|
|
131
142
|
* A way to show a list besides its table: a card per record, its content the built-in one or a component's (`card`),
|
|
132
|
-
* or a project component (`custom`) drawing the rows the list page loads.
|
|
143
|
+
* or a project component (`custom`) drawing the rows the list page loads. A card's `flags` are boolean fields shown as
|
|
144
|
+
* small badges of their labels when true. `name` is what the address names (`?display=cards`); `label` and `icon` are its switch's.
|
|
133
145
|
*/
|
|
134
146
|
export type ListDisplayModel = {
|
|
135
147
|
kind: 'cards';
|
|
136
148
|
name: string;
|
|
137
149
|
label?: string;
|
|
138
150
|
icon?: string;
|
|
151
|
+
image?: string;
|
|
139
152
|
title?: string;
|
|
140
153
|
subtitle?: string;
|
|
141
154
|
badge?: string;
|
|
142
155
|
meta?: string[];
|
|
156
|
+
flags?: string[];
|
|
143
157
|
card?: string;
|
|
144
158
|
} | {
|
|
145
159
|
kind: 'custom';
|
package/dist/view-displays.d.ts
CHANGED
|
@@ -17,16 +17,20 @@ type Naming = {
|
|
|
17
17
|
export declare const tableDisplay = "table";
|
|
18
18
|
export declare const displays: {
|
|
19
19
|
/**
|
|
20
|
-
* A card per record: `
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
20
|
+
* A card per record: `image`, a file field shown as a picture across the card's top, then `title` (default: the
|
|
21
|
+
* view's title) over `subtitle`, `badge` drawn as a badge with its value labels, and a few small `meta` fields such as
|
|
22
|
+
* dates. `flags` are boolean fields shown as small badges of their labels when true, such as a garden or parking.
|
|
23
|
+
* With `card`, a component of the project or a plugin draws each card's content instead, from the row of those
|
|
24
|
+
* fields (`{ row }`); without it, or when it throws, the card is the built-in one. Named `cards` unless `name` says
|
|
25
|
+
* otherwise.
|
|
24
26
|
*/
|
|
25
27
|
cards: (fields: {
|
|
28
|
+
image?: Ref;
|
|
26
29
|
title?: Ref;
|
|
27
30
|
subtitle?: Ref;
|
|
28
31
|
badge?: Ref;
|
|
29
32
|
meta?: Ref[];
|
|
33
|
+
flags?: Ref[];
|
|
30
34
|
}, o?: Naming & {
|
|
31
35
|
card?: ComponentRef<never>;
|
|
32
36
|
}) => ListDisplayModel;
|
package/dist/view-displays.js
CHANGED
|
@@ -13,19 +13,23 @@ const customComponent = (component, where) => {
|
|
|
13
13
|
};
|
|
14
14
|
const displays = {
|
|
15
15
|
/**
|
|
16
|
-
* A card per record: `
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
16
|
+
* A card per record: `image`, a file field shown as a picture across the card's top, then `title` (default: the
|
|
17
|
+
* view's title) over `subtitle`, `badge` drawn as a badge with its value labels, and a few small `meta` fields such as
|
|
18
|
+
* dates. `flags` are boolean fields shown as small badges of their labels when true, such as a garden or parking.
|
|
19
|
+
* With `card`, a component of the project or a plugin draws each card's content instead, from the row of those
|
|
20
|
+
* fields (`{ row }`); without it, or when it throws, the card is the built-in one. Named `cards` unless `name` says
|
|
21
|
+
* otherwise.
|
|
20
22
|
*/
|
|
21
23
|
cards: (fields, o = {}) => ({
|
|
22
24
|
kind: "cards",
|
|
23
25
|
name: o.name ?? "cards",
|
|
24
26
|
...naming(o),
|
|
27
|
+
...fields.image && { image: fields.image.name },
|
|
25
28
|
...fields.title && { title: fields.title.name },
|
|
26
29
|
...fields.subtitle && { subtitle: fields.subtitle.name },
|
|
27
30
|
...fields.badge && { badge: fields.badge.name },
|
|
28
31
|
...fields.meta && { meta: fields.meta.map((ref) => ref.name) },
|
|
32
|
+
...fields.flags && { flags: fields.flags.map((ref) => ref.name) },
|
|
29
33
|
...o.card && { card: customComponent(o.card, "d.cards card") }
|
|
30
34
|
}),
|
|
31
35
|
/**
|
|
@@ -55,10 +59,12 @@ const displayErrors = (list) => {
|
|
|
55
59
|
};
|
|
56
60
|
/** Every field a display names: what it shows, and the field references in a custom display's props. */
|
|
57
61
|
const displayFields = (display) => display.kind === "cards" ? [
|
|
62
|
+
display.image,
|
|
58
63
|
display.title,
|
|
59
64
|
display.subtitle,
|
|
60
65
|
display.badge,
|
|
61
|
-
...display.meta ?? []
|
|
66
|
+
...display.meta ?? [],
|
|
67
|
+
...display.flags ?? []
|
|
62
68
|
].filter((name) => name !== void 0) : [...display.fields, ...fieldRefsIn(display.props)];
|
|
63
69
|
//#endregion
|
|
64
70
|
export { customComponent, displayErrors, displayFields, displays, tableDisplay };
|
package/dist/view-field.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FieldFormat, FieldViewModel, JsonValue } from "./model.js";
|
|
1
|
+
import { BadgeShape, FieldFormat, FieldViewModel, JsonValue } from "./model.js";
|
|
2
2
|
//#region src/view-field.d.ts
|
|
3
3
|
export declare class FieldView<N extends string = string, V = unknown> {
|
|
4
4
|
readonly name: N;
|
|
@@ -8,9 +8,17 @@ export declare class FieldView<N extends string = string, V = unknown> {
|
|
|
8
8
|
help(help: string): FieldView<N, V>;
|
|
9
9
|
format(format: FieldFormat): FieldView<N, V>;
|
|
10
10
|
prefix(prefix: string): FieldView<N, V>;
|
|
11
|
+
/** Text after a number wherever it is shown, such as a unit: `.suffix(' m²')`. */
|
|
12
|
+
suffix(suffix: string): FieldView<N, V>;
|
|
11
13
|
decimals(decimals: number): FieldView<N, V>;
|
|
12
14
|
/** Labels for an enum's values, such as `{ co_signer: 'Co-signer' }`; values left out read as themselves in words. */
|
|
13
15
|
valueLabels(labels: { readonly [P in Extract<V, string>]?: string; }): FieldView<N, V>;
|
|
16
|
+
/** A hex colour per enum value that fills its badge, such as `{ a: '#00a651' }`; values left out keep their tone. */
|
|
17
|
+
valueColors(colors: { readonly [P in Extract<V, string>]?: string; }): FieldView<N, V>;
|
|
18
|
+
/** `arrow` draws the field's badges as arrows pointing right, as on a rating scale such as the EU energy label. */
|
|
19
|
+
badgeShape(badgeShape: BadgeShape): FieldView<N, V>;
|
|
20
|
+
/** An icon before the value on a card's meta row, by the names actions take, such as `ruler`. */
|
|
21
|
+
icon(icon: string): FieldView<N, V>;
|
|
14
22
|
/**
|
|
15
23
|
* Shows and edits the field with the field type a plugin registers under `name` (`definePlugin({ fields })`), given
|
|
16
24
|
* `props`, such as `r.score.widget('rating', { max: 5 })`. Without that plugin, or when the field type does not show
|
package/dist/view-field.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
//#region src/view-field.ts
|
|
2
|
+
const hexColor = /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i;
|
|
2
3
|
var FieldView = class FieldView {
|
|
3
4
|
name;
|
|
4
5
|
props;
|
|
@@ -18,6 +19,10 @@ var FieldView = class FieldView {
|
|
|
18
19
|
prefix(prefix) {
|
|
19
20
|
return this.with({ prefix });
|
|
20
21
|
}
|
|
22
|
+
/** Text after a number wherever it is shown, such as a unit: `.suffix(' m²')`. */
|
|
23
|
+
suffix(suffix) {
|
|
24
|
+
return this.with({ suffix });
|
|
25
|
+
}
|
|
21
26
|
decimals(decimals) {
|
|
22
27
|
return this.with({ decimals });
|
|
23
28
|
}
|
|
@@ -28,6 +33,24 @@ var FieldView = class FieldView {
|
|
|
28
33
|
...labels
|
|
29
34
|
} });
|
|
30
35
|
}
|
|
36
|
+
/** A hex colour per enum value that fills its badge, such as `{ a: '#00a651' }`; values left out keep their tone. */
|
|
37
|
+
valueColors(colors) {
|
|
38
|
+
const entries = Object.entries(colors);
|
|
39
|
+
const wrong = entries.find(([, color]) => !hexColor.test(color));
|
|
40
|
+
if (wrong) throw new Error(`Field "${this.name}": value colour "${wrong[1]}" of "${wrong[0]}" is not a hex colour such as #00a651`);
|
|
41
|
+
return this.with({ valueColors: {
|
|
42
|
+
...this.props.valueColors,
|
|
43
|
+
...Object.fromEntries(entries)
|
|
44
|
+
} });
|
|
45
|
+
}
|
|
46
|
+
/** `arrow` draws the field's badges as arrows pointing right, as on a rating scale such as the EU energy label. */
|
|
47
|
+
badgeShape(badgeShape) {
|
|
48
|
+
return this.with({ badgeShape });
|
|
49
|
+
}
|
|
50
|
+
/** An icon before the value on a card's meta row, by the names actions take, such as `ruler`. */
|
|
51
|
+
icon(icon) {
|
|
52
|
+
return this.with({ icon });
|
|
53
|
+
}
|
|
31
54
|
/**
|
|
32
55
|
* Shows and edits the field with the field type a plugin registers under `name` (`definePlugin({ fields })`), given
|
|
33
56
|
* `props`, such as `r.score.widget('rating', { max: 5 })`. Without that plugin, or when the field type does not show
|
package/dist/view.js
CHANGED
|
@@ -97,6 +97,9 @@ var ViewBuilder = class ViewBuilder {
|
|
|
97
97
|
for (const display of resource ? list?.displays ?? [] : []) {
|
|
98
98
|
const unknown = displayFields(display).find((name) => !Object.hasOwn(resource.fields, name));
|
|
99
99
|
if (unknown !== void 0) throw new Error(`View "${this.state.resource}": display "${display.name}" names field "${unknown}", which the resource does not have`);
|
|
100
|
+
if (display.kind === "cards" && display.image !== void 0 && resource.fields[display.image].type !== "file") throw new Error(`View "${this.state.resource}": display "${display.name}" shows "${display.image}" as its image, which is not a file field`);
|
|
101
|
+
const flag = display.kind === "cards" ? display.flags?.find((name) => resource.fields[name].type !== "boolean") : void 0;
|
|
102
|
+
if (flag !== void 0) throw new Error(`View "${this.state.resource}": display "${display.name}" shows "${flag}" as a flag, which is not a boolean field`);
|
|
100
103
|
}
|
|
101
104
|
const recentErrors = resource && nav?.recent ? navRecentErrors(nav.recent, resource) : [];
|
|
102
105
|
if (recentErrors.length > 0) throw new Error(`View "${this.state.resource}": nav.recent ${recentErrors.join("; ")}`);
|