@protobase/schema 0.18.0 → 0.19.1

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.
@@ -19,6 +19,8 @@ export declare const addressWords: {
19
19
  readonly savedSearch: "saved_search";
20
20
  /** The second path segment that opens a resource's create form instead of a record. */
21
21
  readonly create: "new";
22
+ /** A record's form, `edit=true`, on a view whose record page opens as a read view or locked. */
23
+ readonly edit: "edit";
22
24
  };
23
25
  /** The path of `address` below `basePath` (such as `/admin`), every name and key encoded the one way the app reads them. */
24
26
  export declare const appPath: (address: AppAddress, basePath?: string) => string;
@@ -18,7 +18,9 @@ const addressWords = {
18
18
  /** The saved search a list was opened from, by its key. */
19
19
  savedSearch: "saved_search",
20
20
  /** The second path segment that opens a resource's create form instead of a record. */
21
- create: "new"
21
+ create: "new",
22
+ /** A record's form, `edit=true`, on a view whose record page opens as a read view or locked. */
23
+ edit: "edit"
22
24
  };
23
25
  const listWords = [
24
26
  "display",
@@ -54,7 +56,7 @@ const appPath = (address, basePath = "") => {
54
56
  switch (address.kind) {
55
57
  case "page": return `${basePath}/${encodeURIComponent(address.page)}`;
56
58
  case "create": return `${basePath}/${encodeURIComponent(address.resource)}/${addressWords.create}${valuesQuery(address.values)}`;
57
- case "record": return `${basePath}/${encodeURIComponent(address.resource)}/${encodeURIComponent(encodeKey(address.key))}`;
59
+ case "record": return `${basePath}/${encodeURIComponent(address.resource)}/${encodeURIComponent(encodeKey(address.key))}${address.edit ? `?${addressWords.edit}=true` : ""}`;
58
60
  case "list": return `${basePath}/${encodeURIComponent(address.resource)}${listQuery(address)}`;
59
61
  }
60
62
  };
@@ -96,7 +98,8 @@ const readAppPath = (pathAndQuery, pages) => {
96
98
  if (second !== void 0) return {
97
99
  kind: "record",
98
100
  resource: first,
99
- key: second
101
+ key: second,
102
+ ...url.searchParams.get(addressWords.edit) === "true" && { edit: true }
100
103
  };
101
104
  const params = url.searchParams;
102
105
  const display = params.get(addressWords.display) || void 0;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { FileOptions, FileProcessor, FileProcessorInput, ImageFormat, ImageVariant, ImageVariantOptions, imageFormats } from "./files/file-options.js";
2
- import { ActionModel, ActionRun, AppAddress, BadgeShape, BottomBarItemModel, BottomBarModel, CardFavoriteModel, CardPhotosModel, 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, MapSectionModel, MapView, NavModel, NavRecentModel, NowValue, OrderByItem, RelatedModel, ResourceModel, SaveFeedback, SaveSearchModel, SearchMatch, SearchResultModel, SearchesOfModel, SignInPromptModel, SignInViewModel, SortSpec, Span, StatusTone, UserMenuItemModel, UserMenuModel, ViewModel } from "./model.js";
2
+ import { ActionModel, ActionRun, AppAddress, BadgeShape, BottomBarItemModel, BottomBarModel, CardFavoriteModel, CardPhotosModel, 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, GalleryModel, HelpWhen, JsonValue, KeyValue, LayoutItemModel, ListDisplayModel, ListModel, ListQuery, Literal, MapSectionModel, MapView, NavModel, NavRecentModel, NowValue, OrderByItem, RecordMode, RelatedModel, ResourceModel, SaveFeedback, SaveSearchModel, SearchMatch, SearchResultModel, SearchesOfModel, SignInPromptModel, SignInViewModel, SortSpec, Span, StatusTone, UserMenuItemModel, UserMenuModel, ViewModel } from "./model.js";
3
3
  import { AccessAction, AccessContext, AccessFn, AccessResult, AccessUser, FieldAccess, FieldRule } from "./access/types.js";
4
4
  import { AnyField, Constraint, Field, FieldMeta, FieldTypeDef } from "./field.js";
5
5
  import { RelationField } from "./relation-field.js";
@@ -37,4 +37,4 @@ import { AccessSource, ResolveOptions, ResolvedAccess, RowFilterOperation, acces
37
37
  import { restrictModel } from "./access/restrict.js";
38
38
  import "./access/index.js";
39
39
  import { AssistantAction, AssistantActionRequest, AssistantCardPart, AssistantDiff, AssistantEvent, AssistantMessage, AssistantMessageRequest, AssistantPart, AssistantState, AssistantStep, AssistantStepState, AssistantTablePart, AssistantTextPart, AssistantTone, AssistantWidgetPart } from "./assistant/protocol.js";
40
- export { type Access, type AccessAction, type AccessContext, type AccessFn, type AccessResult, type AccessSource, type AccessUser, type ActionModel, type ActionRun, type AnyField, 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 CardFavoriteModel, type CardPhotosModel, type ChartGranularity, type ChartModel, type ChartRange, type CheckResult, type CheckedFilter, type CompareOp, type ComponentRef, type Constraint, type CreatedOptions, type CustomSectionModel, type DatePreset, type DeletedOptions, 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 FieldMeta, type FieldModel, type FieldPath, type FieldRefValue, type FieldRefs, type FieldRule, type FieldType, type FieldTypeDef, type FieldView, type FieldViewModel, type FieldViews, type FieldWidgetModel, type FieldsInput, type FileField, type FileFieldModel, type FileOptions, type FileProcessor, type FileProcessorInput, type FilterError, type FilterErrorCode, type FilterExpr, type FilterLayout, type FilterLimits, type FilterOf, type FilterParse, type FilterValue, type FilterWidgetModel, type Grant, type HeldCapability, type ImageFormat, type ImageVariant, type ImageVariantOptions, type InferRecord, type Issue, type JsonValue, type KeyOf, type KeyRefs, type KeyType, type KeyValue, type LayoutItemModel, type ListDisplayModel, type ListModel, type ListQuery, type Literal, type MapSectionModel, type MapView, type NavModel, type NavRecentModel, type NowValue, type OrderByCheck, type OrderByItem, type OrderByParse, type RecordKey, type RecordOf, type Ref, type Refs, type RelatedModel, type RelationField, type ResolveOptions, type ResolvedAccess, type ResourceBuilder, type ResourceModel, type RoleEntry, type RoleLimit, type Roles, type RolesOptions, type RowFilterOperation, type Rule, type SaveFeedback, type SaveSearchModel, type SearchMatch, type SearchRef, type SearchRefs, type SearchResultModel, type SearchesOfModel, type SignInAddress, type SignInPromptModel, type SignInStep, type SignInViewModel, type SortSpec, type Span, type StatusTone, type UndeletedOptions, type UpdatedOptions, type UserMenuItemModel, type UserMenuModel, type UserMenuSource, type Validator, type ViewBuilder, type ViewModel, type ViewProps, accessWarnings, addressWords, anonymousRole, appPath, bottomBar, bottomBarErrors, checkFilter, checkOrderBy, checkRecord, createWhere, defineRoles, enumLabel, evaluateFilter, f, fieldRef, fieldsUsedBy, imageFormats, keyTypes, layout as l, maxBottomBarItems, parseFilter, parseOrderBy, pickView, printFilter, printOrderBy, readAppPath, readSignInAddress, resolveAccess, resource, restrictModel, rule, signInParams, signInPath, signInRoute, signInSteps, userMenu, view, where };
40
+ export { type Access, type AccessAction, type AccessContext, type AccessFn, type AccessResult, type AccessSource, type AccessUser, type ActionModel, type ActionRun, type AnyField, 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 CardFavoriteModel, type CardPhotosModel, type ChartGranularity, type ChartModel, type ChartRange, type CheckResult, type CheckedFilter, type CompareOp, type ComponentRef, type Constraint, type CreatedOptions, type CustomSectionModel, type DatePreset, type DeletedOptions, 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 FieldMeta, type FieldModel, type FieldPath, type FieldRefValue, type FieldRefs, type FieldRule, type FieldType, type FieldTypeDef, type FieldView, type FieldViewModel, type FieldViews, type FieldWidgetModel, type FieldsInput, type FileField, type FileFieldModel, type FileOptions, type FileProcessor, type FileProcessorInput, type FilterError, type FilterErrorCode, type FilterExpr, type FilterLayout, type FilterLimits, type FilterOf, type FilterParse, type FilterValue, type FilterWidgetModel, type GalleryModel, type Grant, type HeldCapability, type HelpWhen, type ImageFormat, type ImageVariant, type ImageVariantOptions, type InferRecord, type Issue, type JsonValue, type KeyOf, type KeyRefs, type KeyType, type KeyValue, type LayoutItemModel, type ListDisplayModel, type ListModel, type ListQuery, type Literal, type MapSectionModel, type MapView, type NavModel, type NavRecentModel, type NowValue, type OrderByCheck, type OrderByItem, type OrderByParse, type RecordKey, type RecordMode, type RecordOf, type Ref, type Refs, type RelatedModel, type RelationField, type ResolveOptions, type ResolvedAccess, type ResourceBuilder, type ResourceModel, type RoleEntry, type RoleLimit, type Roles, type RolesOptions, type RowFilterOperation, type Rule, type SaveFeedback, type SaveSearchModel, type SearchMatch, type SearchRef, type SearchRefs, type SearchResultModel, type SearchesOfModel, type SignInAddress, type SignInPromptModel, type SignInStep, type SignInViewModel, type SortSpec, type Span, type StatusTone, type UndeletedOptions, type UpdatedOptions, type UserMenuItemModel, type UserMenuModel, type UserMenuSource, type Validator, type ViewBuilder, type ViewModel, type ViewProps, accessWarnings, addressWords, anonymousRole, appPath, bottomBar, bottomBarErrors, checkFilter, checkOrderBy, checkRecord, createWhere, defineRoles, enumLabel, evaluateFilter, f, fieldRef, fieldsUsedBy, imageFormats, keyTypes, layout as l, maxBottomBarItems, parseFilter, parseOrderBy, pickView, printFilter, printOrderBy, readAppPath, readSignInAddress, resolveAccess, resource, restrictModel, rule, signInParams, signInPath, signInRoute, signInSteps, userMenu, view, where };
package/dist/layout.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { LayoutItemModel, RelatedModel } from "./model.js";
1
+ import { CardPhotosModel, HelpWhen, LayoutItemModel, RelatedModel } from "./model.js";
2
2
  import { Ref } from "./refs.js";
3
3
  import { ComponentRef, ViewProps } from "./view-displays.js";
4
4
  //#region src/layout.d.ts
@@ -16,9 +16,25 @@ import { ComponentRef, ViewProps } from "./view-displays.js";
16
16
  * ```
17
17
  */
18
18
  export declare const layout: {
19
- /** A titled group of fields in the main column, editable where the user may write; `help` is a line under the title. */
19
+ /**
20
+ * A titled group of fields in the main column, editable where the user may write; `help` is a line under the title,
21
+ * shown only while the record is edited with `helpWhen: 'editing'` (the view's `.helpWhen(...)` when omitted).
22
+ */
20
23
  section: (title: string, fields: Ref[], o?: {
21
24
  help?: string;
25
+ helpWhen?: HelpWhen;
26
+ }) => LayoutItemModel;
27
+ /**
28
+ * The record's photos as tiles in the main column, one large and the next four smaller, the last saying how many more
29
+ * there are, each opening a lightbox that steps through them all: first `image`, a file field of the record, then the
30
+ * `image` of the records of `photos.resource` whose relation `photos.field` points at the record, in `photos.sort`
31
+ * order, as a cards display's `photos` names them. In the lightbox, a user who may read a photo's record opens its
32
+ * details. Throws without `image` or `photos`. See
33
+ * [Galleries](website/src/content/docs/reference/ui-config.mdx#galleries).
34
+ */
35
+ gallery: (title: string, o: {
36
+ image?: Ref;
37
+ photos?: CardPhotosModel;
22
38
  }) => LayoutItemModel;
23
39
  /** A read-only summary card of fields beside the main column. */
24
40
  sidebar: (fields: Ref[]) => LayoutItemModel;
package/dist/layout.js CHANGED
@@ -15,13 +15,34 @@ import { customComponent } from "./view-displays.js";
15
15
  * ```
16
16
  */
17
17
  const layout = {
18
- /** A titled group of fields in the main column, editable where the user may write; `help` is a line under the title. */
18
+ /**
19
+ * A titled group of fields in the main column, editable where the user may write; `help` is a line under the title,
20
+ * shown only while the record is edited with `helpWhen: 'editing'` (the view's `.helpWhen(...)` when omitted).
21
+ */
19
22
  section: (title, fields, o = {}) => ({
20
23
  kind: "section",
21
24
  title,
22
25
  fields: fields.map((ref) => ref.name),
23
- ...o.help !== void 0 && { help: o.help }
26
+ ...o.help !== void 0 && { help: o.help },
27
+ ...o.helpWhen !== void 0 && { helpWhen: o.helpWhen }
24
28
  }),
29
+ /**
30
+ * The record's photos as tiles in the main column, one large and the next four smaller, the last saying how many more
31
+ * there are, each opening a lightbox that steps through them all: first `image`, a file field of the record, then the
32
+ * `image` of the records of `photos.resource` whose relation `photos.field` points at the record, in `photos.sort`
33
+ * order, as a cards display's `photos` names them. In the lightbox, a user who may read a photo's record opens its
34
+ * details. Throws without `image` or `photos`. See
35
+ * [Galleries](website/src/content/docs/reference/ui-config.mdx#galleries).
36
+ */
37
+ gallery: (title, o) => {
38
+ if (!o.image && !o.photos) throw new Error(`l.gallery "${title}" needs an image field, photos or both`);
39
+ return {
40
+ kind: "gallery",
41
+ title,
42
+ ...o.image && { image: o.image.name },
43
+ ...o.photos && { photos: { ...o.photos } }
44
+ };
45
+ },
25
46
  /** A read-only summary card of fields beside the main column. */
26
47
  sidebar: (fields) => ({
27
48
  kind: "sidebar",
package/dist/model.d.ts CHANGED
@@ -188,6 +188,11 @@ export type AppAddress = {
188
188
  resource: string;
189
189
  /** The record's key: its value, or its parts for a composite key. */
190
190
  key: KeyValue | readonly KeyValue[];
191
+ /**
192
+ * The record's form, on a view whose record page opens as a read view or locked (`.recordMode(...)`): the page that
193
+ * Edit opens. A view whose record page is always editable ignores it.
194
+ */
195
+ edit?: boolean;
191
196
  } | {
192
197
  /** The form for a new record. */
193
198
  kind: 'create';
@@ -212,6 +217,8 @@ export type FieldViewModel = {
212
217
  label?: string;
213
218
  /** A line under the field on the record page and in forms. */
214
219
  help?: string;
220
+ /** When `help` shows: always, or only while the record is edited; the view's `helpWhen` when omitted. */
221
+ helpWhen?: HelpWhen;
215
222
  /** How the value is shown. */
216
223
  format?: FieldFormat;
217
224
  /** A text that runs over several lines: forms edit it in a box that grows with it, and its line breaks show. */
@@ -236,6 +243,12 @@ export type FieldViewModel = {
236
243
  */
237
244
  widget?: FieldWidgetModel;
238
245
  };
246
+ /**
247
+ * When a help line shows: `always`, the default, or only while the record is `editing`: on its form and the create
248
+ * form, not on the record page's read view or its locked form. See
249
+ * [The record page](website/src/content/docs/reference/ui-config.mdx#the-record-page).
250
+ */
251
+ export type HelpWhen = 'always' | 'editing';
239
252
  /** How a field's badges are drawn: `arrow` points them right, as on a rating scale. */
240
253
  export type BadgeShape = 'arrow';
241
254
  /** A field type by the name a plugin registers it under, and the plain data it is given. */
@@ -563,12 +576,30 @@ export type LayoutItemModel = {
563
576
  fields: string[];
564
577
  /** A line under the title. */
565
578
  help?: string;
579
+ /** When `help` shows: always, or only while the record is edited; the view's `helpWhen` when omitted. */
580
+ helpWhen?: HelpWhen;
566
581
  } | {
567
582
  /** A read-only summary card beside the main column. */
568
583
  kind: 'sidebar';
569
584
  /** The fields it shows, in order. */
570
585
  fields: string[];
571
- } | RelatedModel | CustomSectionModel | MapSectionModel;
586
+ } | RelatedModel | CustomSectionModel | MapSectionModel | GalleryModel;
587
+ /**
588
+ * A record's photos as tiles in the main column under `title`, one large and the next few smaller, opening a lightbox
589
+ * that steps through them all: first the record's own file field `image`, then the `image` of the records of
590
+ * `photos.resource` whose relation `photos.field` points at it, in `photos.sort` order, as a cards display's `photos`
591
+ * names them. See [Galleries](website/src/content/docs/reference/ui-config.mdx#galleries).
592
+ */
593
+ export type GalleryModel = {
594
+ /** A gallery of photos. */
595
+ kind: 'gallery';
596
+ /** The section's title. */
597
+ title: string;
598
+ /** The record's own file field, shown first. */
599
+ image?: string;
600
+ /** The records holding the further photos. */
601
+ photos?: CardPhotosModel;
602
+ };
572
603
  /** How far back a list's chart reaches. */
573
604
  export type ChartRange = '7d' | '30d' | '90d' | '1y' | 'all';
574
605
  /** How long each bar of a list's chart is. */
@@ -582,6 +613,13 @@ export type ChartModel = {
582
613
  /** How long each bar is. */
583
614
  granularity: ChartGranularity;
584
615
  };
616
+ /**
617
+ * How the record page opens to a user who may change the record: `edit`, the default, as its form, ready to change;
618
+ * `read` as a read view of label/value tables, with an Edit button to the form; `locked` as the same form, locked until
619
+ * Edit is pressed, where reading and editing look alike. A user who may not change it sees the read view with `read`,
620
+ * and the locked form otherwise.
621
+ */
622
+ export type RecordMode = 'edit' | 'read' | 'locked';
585
623
  /** How the record page shows that a save went through: a `toast` (the default) or on the Save `button`. */
586
624
  export type SaveFeedback = 'toast' | 'button';
587
625
  /**
@@ -775,6 +813,10 @@ export type ViewModel = {
775
813
  layout: LayoutItemModel[];
776
814
  /** A chart of records over time above the list. */
777
815
  chart?: ChartModel;
816
+ /** How the record page opens: as its form, a read view or a locked form; as its form when omitted. */
817
+ recordMode?: RecordMode;
818
+ /** When the help lines of its fields and sections show, unless their own `helpWhen` says; always when omitted. */
819
+ helpWhen?: HelpWhen;
778
820
  /** How the record page shows that a save went through. */
779
821
  saveFeedback?: SaveFeedback;
780
822
  /** The named actions of its records. */
@@ -1,4 +1,4 @@
1
- import { BadgeShape, FieldFormat, FieldViewModel, JsonValue } from "./model.js";
1
+ import { BadgeShape, FieldFormat, FieldViewModel, HelpWhen, JsonValue } from "./model.js";
2
2
  //#region src/view-field.d.ts
3
3
  /** How a view shows one field, as `.fields((r) => ...)` hands it out: each hint returns a new field view. */
4
4
  export declare class FieldView<N extends string = string, V = unknown> {
@@ -14,8 +14,13 @@ export declare class FieldView<N extends string = string, V = unknown> {
14
14
  props?: FieldViewModel);
15
15
  /** What the field is called, instead of its name in words. */
16
16
  label(label: string): FieldView<N, V>;
17
- /** A line under the field on the record page and in forms. */
18
- help(help: string): FieldView<N, V>;
17
+ /**
18
+ * A line under the field on the record page and in forms; `when: 'editing'` shows it only while the record is edited,
19
+ * not on its read view (the view's `.helpWhen(...)` when omitted).
20
+ */
21
+ help(help: string, o?: {
22
+ when?: HelpWhen;
23
+ }): FieldView<N, V>;
19
24
  /** How the value shows: `badge` for statuses, `code` in monospace, `percent` with a % sign. */
20
25
  format(format: FieldFormat): FieldView<N, V>;
21
26
  /**
@@ -13,9 +13,15 @@ var FieldView = class FieldView {
13
13
  label(label) {
14
14
  return this.with({ label });
15
15
  }
16
- /** A line under the field on the record page and in forms. */
17
- help(help) {
18
- return this.with({ help });
16
+ /**
17
+ * A line under the field on the record page and in forms; `when: 'editing'` shows it only while the record is edited,
18
+ * not on its read view (the view's `.helpWhen(...)` when omitted).
19
+ */
20
+ help(help, o = {}) {
21
+ return this.with({
22
+ help,
23
+ ...o.when !== void 0 && { helpWhen: o.when }
24
+ });
19
25
  }
20
26
  /** How the value shows: `badge` for statuses, `code` in monospace, `percent` with a % sign. */
21
27
  format(format) {
package/dist/view.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ActionModel, ChartGranularity, ChartRange, FilterWidgetModel, LayoutItemModel, ListDisplayModel, NavModel, NavRecentModel, ResourceModel, SaveFeedback, SaveSearchModel, SignInPromptModel, ViewModel } from "./model.js";
1
+ import { ActionModel, ChartGranularity, ChartRange, FilterWidgetModel, HelpWhen, LayoutItemModel, ListDisplayModel, NavModel, NavRecentModel, RecordMode, ResourceModel, SaveFeedback, SaveSearchModel, SignInPromptModel, ViewModel } from "./model.js";
2
2
  import { FieldRefs, Ref } from "./refs.js";
3
3
  import { actions } from "./view-actions.js";
4
4
  import { displays } from "./view-displays.js";
@@ -91,6 +91,18 @@ export declare class ViewBuilder<R extends {
91
91
  range: ChartRange;
92
92
  granularity: ChartGranularity;
93
93
  }): ViewBuilder<R>;
94
+ /**
95
+ * How the record page opens to a user who may change the record: `edit`, the default, as its form; `read` as a read
96
+ * view, its sections as tables of labels and values, with an Edit button to the form; `locked` as the form, locked
97
+ * until Edit is pressed. The form has its own address (`edit: true`), so it opens in a new tab and Back leaves it. See
98
+ * [The record page](website/src/content/docs/reference/ui-config.mdx#read-view-and-edit).
99
+ */
100
+ recordMode(recordMode: RecordMode): ViewBuilder<R>;
101
+ /**
102
+ * When the help lines of the view's fields and sections show: `always`, the default, or only while the record is
103
+ * `editing`, on its form; a field's `.help(text, { when })` or a section's `helpWhen` says otherwise for its own.
104
+ */
105
+ helpWhen(helpWhen: HelpWhen): ViewBuilder<R>;
94
106
  /** Where the record page shows that it saved: a toast (the default) or the Save button. */
95
107
  saveFeedback(saveFeedback: SaveFeedback): ViewBuilder<R>;
96
108
  /**
@@ -139,7 +151,7 @@ export declare class ViewBuilder<R extends {
139
151
  };
140
152
  limit?: number;
141
153
  }): ViewBuilder<R>;
142
- /** With the resource's model, also verifies the title, search result and list display fields, `listOf`, `searchesOf` and the sidebar's `recent` group. */
154
+ /** With the resource's model, also verifies the title, search result and list display fields, a gallery's image, `listOf`, `searchesOf` and the sidebar's `recent` group. */
143
155
  toModel(resource?: ResourceModel): ViewModel;
144
156
  private refs;
145
157
  private next;
package/dist/view.js CHANGED
@@ -111,6 +111,22 @@ var ViewBuilder = class ViewBuilder {
111
111
  granularity
112
112
  } });
113
113
  }
114
+ /**
115
+ * How the record page opens to a user who may change the record: `edit`, the default, as its form; `read` as a read
116
+ * view, its sections as tables of labels and values, with an Edit button to the form; `locked` as the form, locked
117
+ * until Edit is pressed. The form has its own address (`edit: true`), so it opens in a new tab and Back leaves it. See
118
+ * [The record page](website/src/content/docs/reference/ui-config.mdx#read-view-and-edit).
119
+ */
120
+ recordMode(recordMode) {
121
+ return this.next({ recordMode });
122
+ }
123
+ /**
124
+ * When the help lines of the view's fields and sections show: `always`, the default, or only while the record is
125
+ * `editing`, on its form; a field's `.help(text, { when })` or a section's `helpWhen` says otherwise for its own.
126
+ */
127
+ helpWhen(helpWhen) {
128
+ return this.next({ helpWhen });
129
+ }
114
130
  /** Where the record page shows that it saved: a toast (the default) or the Save button. */
115
131
  saveFeedback(saveFeedback) {
116
132
  return this.next({ saveFeedback });
@@ -174,7 +190,7 @@ var ViewBuilder = class ViewBuilder {
174
190
  };
175
191
  return this.next({ searchesOf: searches });
176
192
  }
177
- /** With the resource's model, also verifies the title, search result and list display fields, `listOf`, `searchesOf` and the sidebar's `recent` group. */
193
+ /** With the resource's model, also verifies the title, search result and list display fields, a gallery's image, `listOf`, `searchesOf` and the sidebar's `recent` group. */
178
194
  toModel(resource) {
179
195
  const { title, nav, searchResult, list, listOf } = this.state;
180
196
  if (resource && title !== void 0 && !Object.hasOwn(resource.fields, title)) throw new Error(`View "${this.state.resource}": title field "${title}" does not exist on the resource`);
@@ -187,6 +203,11 @@ var ViewBuilder = class ViewBuilder {
187
203
  const flag = display.kind === "cards" ? display.flags?.find((name) => resource.fields[name].type !== "boolean") : void 0;
188
204
  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`);
189
205
  }
206
+ for (const item of resource ? this.state.layout : []) {
207
+ if (item.kind !== "gallery" || item.image === void 0) continue;
208
+ if (!Object.hasOwn(resource.fields, item.image)) throw new Error(`View "${this.state.resource}": gallery "${item.title}" names field "${item.image}", which the resource does not have`);
209
+ if (resource.fields[item.image].type !== "file") throw new Error(`View "${this.state.resource}": gallery "${item.title}" shows "${item.image}" as its image, which is not a file field`);
210
+ }
190
211
  if (resource && listOf !== void 0 && (!Object.hasOwn(resource.fields, listOf) || resource.fields[listOf].type !== "relation")) throw new Error(`View "${this.state.resource}": listOf names "${listOf}", which is not a relation field of the resource`);
191
212
  const searchErrors = resource && this.state.searchesOf ? searchesOfErrors(this.state.searchesOf, resource) : [];
192
213
  if (searchErrors.length > 0) throw new Error(`View "${this.state.resource}": searchesOf ${searchErrors.join("; ")}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@protobase/schema",
3
- "version": "0.18.0",
3
+ "version": "0.19.1",
4
4
  "description": "Resource, field, view and access definitions for Protobase configs.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {