@protobase/schema 0.22.5 → 0.22.7

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.
@@ -5,6 +5,8 @@ import { AppAddress } from "./model.js";
5
5
  * changes in this module alone.
6
6
  */
7
7
  export declare const addressWords: {
8
+ /** A named list of the resource (`.namedList(...)`), in place of its own list. */
9
+ readonly list: "list";
8
10
  /** A list's filter (AIP-160), as the list API takes it. An empty `filter=` means no filter, in place of the view's default. */
9
11
  readonly filter: "filter";
10
12
  /** A list's order (AIP-132). */
@@ -5,6 +5,8 @@ import { encodeKey } from "./keys.js";
5
5
  * changes in this module alone.
6
6
  */
7
7
  const addressWords = {
8
+ /** A named list of the resource (`.namedList(...)`), in place of its own list. */
9
+ list: "list",
8
10
  /** A list's filter (AIP-160), as the list API takes it. An empty `filter=` means no filter, in place of the view's default. */
9
11
  filter: "filter",
10
12
  /** A list's order (AIP-132). */
@@ -27,6 +29,7 @@ const addressWords = {
27
29
  open: "open"
28
30
  };
29
31
  const listWords = [
32
+ "list",
30
33
  "display",
31
34
  "filter",
32
35
  "orderBy",
@@ -136,6 +139,7 @@ const readAppPath = (pathAndQuery, pages) => {
136
139
  };
137
140
  }
138
141
  const params = url.searchParams;
142
+ const list = params.get(addressWords.list) || void 0;
139
143
  const display = params.get(addressWords.display) || void 0;
140
144
  const filter = params.get(addressWords.filter) ?? void 0;
141
145
  const orderBy = params.get(addressWords.orderBy) || void 0;
@@ -146,6 +150,7 @@ const readAppPath = (pathAndQuery, pages) => {
146
150
  return {
147
151
  kind: "list",
148
152
  resource: first,
153
+ ...list && { list },
149
154
  ...display && { display },
150
155
  ...filter !== void 0 && { filter },
151
156
  ...orderBy && { orderBy },
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 { ActionInputKind, ActionInputModel, ActionModel, ActionRun, AppAddress, BadgeShape, BottomBarItemModel, BottomBarModel, CalendarView, CardFavoriteModel, CardPhotosModel, ChartGranularity, ChartModel, ChartRange, CheckedFilter, CommentsSectionModel, CompareOp, CreateModel, CustomSectionModel, DatePreset, DurationLiteral, DurationUnit, EventModel, EventOperation, ExportModel, FieldFormat, FieldModel, FieldPath, FieldType, FieldViewModel, FieldWidgetModel, FileFieldModel, FilterError, FilterErrorCode, FilterExpr, FilterLayout, FilterOf, FilterValue, FilterWidgetModel, FormModel, GalleryModel, GroupChartModel, GroupItemModel, GroupModel, HelpWhen, HistorySectionModel, JsonValue, KeyValue, LayoutItemModel, ListDisplayModel, ListModel, ListQuery, Literal, MapSectionModel, MapView, NavModel, NavRecentModel, NowValue, OrderByItem, PeriodChartModel, RecordMode, RecordWhen, RelatedColumnModel, RelatedModel, ResourceModel, SaveFeedback, SaveSearchModel, SearchMatch, SearchResultModel, SearchesOfModel, SectionModel, SectionPresentation, ShowWhen, SignInPromptModel, SignInViewModel, SortSpec, Span, StatusTone, UserMenuItemModel, UserMenuModel, ViewModel } from "./model.js";
2
+ import { ActionInputKind, ActionInputModel, ActionModel, ActionRun, AppAddress, BadgeShape, BottomBarItemModel, BottomBarModel, CalendarView, CardFavoriteModel, CardPhotosModel, ChartGranularity, ChartModel, ChartRange, CheckedFilter, CommentsSectionModel, CompareOp, CreateModel, CustomSectionModel, DatePreset, DurationLiteral, DurationUnit, EventModel, EventOperation, ExportModel, FieldFormat, FieldModel, FieldPath, FieldType, FieldViewModel, FieldWidgetModel, FileFieldModel, FilterError, FilterErrorCode, FilterExpr, FilterLayout, FilterOf, FilterValue, FilterWidgetModel, FormModel, GalleryModel, GroupChartModel, GroupItemModel, GroupModel, HelpWhen, HistorySectionModel, JsonValue, KeyValue, LayoutItemModel, ListDisplayModel, ListModel, ListQuery, Literal, MapSectionModel, MapView, NamedListModel, NavModel, NavRecentModel, NowValue, OrderByItem, PeriodChartModel, RecordMode, RecordWhen, RelatedColumnModel, RelatedModel, ResourceModel, SaveFeedback, SaveSearchModel, SearchMatch, SearchResultModel, SearchesOfModel, SectionModel, SectionPresentation, ShowWhen, 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";
@@ -42,4 +42,4 @@ import { AccessSource, ResolveOptions, ResolvedAccess, RowFilterOperation, acces
42
42
  import { restrictModel } from "./access/restrict.js";
43
43
  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
- export { type Access, type AccessAction, type AccessContext, type AccessFn, type AccessResult, type AccessSource, type AccessUser, type ActionInputKind, type ActionInputModel, 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 CalendarView, type Capability, type CapabilityAction, type CapabilityLimit, type CapabilityScope, type CardFavoriteModel, type CardPhotosModel, type ChartGranularity, type ChartInput, type ChartModel, type ChartRange, type CheckResult, type CheckedFilter, type CommentsSectionModel, type CompareOp, type ComponentRef, type Constraint, type CreateModel, 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 ExportModel, 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 FormAddress, type FormInput, type FormModel, type GalleryModel, type Grant, type GroupChartInput, type GroupChartModel, type GroupItemModel, type GroupModel, type HeldCapability, type HelpWhen, type HistorySectionModel, type ImageFormat, type ImageVariant, type ImageVariantOptions, type InferRecord, type Issue, type JsonValue, type KeyOf, type KeyRefs, type KeyType, type KeyValue, type LayoutItemModel, type ListColumn, 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 PeriodChartInput, type PeriodChartModel, type RecordKey, type RecordMode, type RecordOf, type RecordWhen, type Ref, type Refs, type RelatedColumnModel, 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 SectionModel, type SectionPresentation, type ShowWhen, 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, fitsKeyType, formPath, formRoute, imageFormats, isRecordCondition, keyTypes, layout as l, maxBottomBarItems, parseFilter, parseOrderBy, pickView, printFilter, printOrderBy, readAppPath, readFormAddress, readSignInAddress, resolveAccess, resource, restrictModel, rule, signInParams, signInPath, signInRoute, signInSteps, userMenu, view, where };
45
+ export { type Access, type AccessAction, type AccessContext, type AccessFn, type AccessResult, type AccessSource, type AccessUser, type ActionInputKind, type ActionInputModel, 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 CalendarView, type Capability, type CapabilityAction, type CapabilityLimit, type CapabilityScope, type CardFavoriteModel, type CardPhotosModel, type ChartGranularity, type ChartInput, type ChartModel, type ChartRange, type CheckResult, type CheckedFilter, type CommentsSectionModel, type CompareOp, type ComponentRef, type Constraint, type CreateModel, 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 ExportModel, 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 FormAddress, type FormInput, type FormModel, type GalleryModel, type Grant, type GroupChartInput, type GroupChartModel, type GroupItemModel, type GroupModel, type HeldCapability, type HelpWhen, type HistorySectionModel, type ImageFormat, type ImageVariant, type ImageVariantOptions, type InferRecord, type Issue, type JsonValue, type KeyOf, type KeyRefs, type KeyType, type KeyValue, type LayoutItemModel, type ListColumn, type ListDisplayModel, type ListModel, type ListQuery, type Literal, type MapSectionModel, type MapView, type NamedListModel, type NavModel, type NavRecentModel, type NowValue, type OrderByCheck, type OrderByItem, type OrderByParse, type PeriodChartInput, type PeriodChartModel, type RecordKey, type RecordMode, type RecordOf, type RecordWhen, type Ref, type Refs, type RelatedColumnModel, 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 SectionModel, type SectionPresentation, type ShowWhen, 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, fitsKeyType, formPath, formRoute, imageFormats, isRecordCondition, keyTypes, layout as l, maxBottomBarItems, parseFilter, parseOrderBy, pickView, printFilter, printOrderBy, readAppPath, readFormAddress, readSignInAddress, resolveAccess, resource, restrictModel, rule, signInParams, signInPath, signInRoute, signInSteps, userMenu, view, where };
@@ -19,4 +19,5 @@ import { AddressCheck, addressErrors } from "./address-errors.js";
19
19
  import { applyAssistantEvent, emptyAssistantState } from "./assistant/apply-event.js";
20
20
  import { readEventStream } from "./assistant/event-stream.js";
21
21
  import { layoutItems } from "./layout-items.js";
22
- export { type AddressCheck, type PluginHost, type PluginRequirement, type PropShapes, type RefShape, type SearchTerm, actionInputErrors, addressErrors, addressPlaceholders, appHrefError, applyAssistantEvent, boardErrors, bottomBarFor, calendarErrors, cardPhotoLimit, chartFields, checkEventFilter, checkedAccept, checkedProvider, decodeKey, defaultRecentLimit, displayFields, emptyAssistantState, encodeKey, fieldRefsIn, fillAddress, hrefField, hrefPlaceholders, inRange, isFieldRef, isGroupChart, layoutItems, leavesApp, linkActionError, markFieldRefs, matchedSearchField, matchesSearch, navRecentErrors, parseSize, pluginProblem, promptError, promptFields, pruneByShape, readEventStream, redactSecrets, renderPrompt, searchTerms, shapes, shownInputs, statusTones, tableDisplay, termMatches, unmarkFieldRefs, updateSetErrors, updateValues, webhookResources };
22
+ import { viewOfList } from "./named-list.js";
23
+ export { type AddressCheck, type PluginHost, type PluginRequirement, type PropShapes, type RefShape, type SearchTerm, actionInputErrors, addressErrors, addressPlaceholders, appHrefError, applyAssistantEvent, boardErrors, bottomBarFor, calendarErrors, cardPhotoLimit, chartFields, checkEventFilter, checkedAccept, checkedProvider, decodeKey, defaultRecentLimit, displayFields, emptyAssistantState, encodeKey, fieldRefsIn, fillAddress, hrefField, hrefPlaceholders, inRange, isFieldRef, isGroupChart, layoutItems, leavesApp, linkActionError, markFieldRefs, matchedSearchField, matchesSearch, navRecentErrors, parseSize, pluginProblem, promptError, promptFields, pruneByShape, readEventStream, redactSecrets, renderPrompt, searchTerms, shapes, shownInputs, statusTones, tableDisplay, termMatches, unmarkFieldRefs, updateSetErrors, updateValues, viewOfList, webhookResources };
package/dist/internal.js CHANGED
@@ -3,6 +3,7 @@ import { promptError, promptFields, renderPrompt } from "./ai-prompt.js";
3
3
  import { checkedAccept, checkedProvider, parseSize } from "./files/file-options.js";
4
4
  import { matchedSearchField, matchesSearch, searchTerms, termMatches } from "./search-match.js";
5
5
  import { checkEventFilter } from "./events/event-models.js";
6
+ import { viewOfList } from "./named-list.js";
6
7
  import { defaultRecentLimit, navRecentErrors, statusTones } from "./nav-recent.js";
7
8
  import { actionInputErrors, shownInputs } from "./action-inputs.js";
8
9
  import { updateSetErrors, updateValues } from "./update-set.js";
@@ -19,4 +20,4 @@ import { addressErrors } from "./address-errors.js";
19
20
  import { applyAssistantEvent, emptyAssistantState } from "./assistant/apply-event.js";
20
21
  import { readEventStream } from "./assistant/event-stream.js";
21
22
  import { layoutItems } from "./layout-items.js";
22
- export { actionInputErrors, addressErrors, addressPlaceholders, appHrefError, applyAssistantEvent, boardErrors, bottomBarFor, calendarErrors, cardPhotoLimit, chartFields, checkEventFilter, checkedAccept, checkedProvider, decodeKey, defaultRecentLimit, displayFields, emptyAssistantState, encodeKey, fieldRefsIn, fillAddress, hrefField, hrefPlaceholders, inRange, isFieldRef, isGroupChart, layoutItems, leavesApp, linkActionError, markFieldRefs, matchedSearchField, matchesSearch, navRecentErrors, parseSize, pluginProblem, promptError, promptFields, pruneByShape, readEventStream, redactSecrets, renderPrompt, searchTerms, shapes, shownInputs, statusTones, tableDisplay, termMatches, unmarkFieldRefs, updateSetErrors, updateValues, webhookResources };
23
+ export { actionInputErrors, addressErrors, addressPlaceholders, appHrefError, applyAssistantEvent, boardErrors, bottomBarFor, calendarErrors, cardPhotoLimit, chartFields, checkEventFilter, checkedAccept, checkedProvider, decodeKey, defaultRecentLimit, displayFields, emptyAssistantState, encodeKey, fieldRefsIn, fillAddress, hrefField, hrefPlaceholders, inRange, isFieldRef, isGroupChart, layoutItems, leavesApp, linkActionError, markFieldRefs, matchedSearchField, matchesSearch, navRecentErrors, parseSize, pluginProblem, promptError, promptFields, pruneByShape, readEventStream, redactSecrets, renderPrompt, searchTerms, shapes, shownInputs, statusTones, tableDisplay, termMatches, unmarkFieldRefs, updateSetErrors, updateValues, viewOfList, webhookResources };
package/dist/model.d.ts CHANGED
@@ -189,6 +189,8 @@ export type AppAddress = {
189
189
  kind: 'list';
190
190
  /** The resource listed. */
191
191
  resource: string;
192
+ /** A named list of the resource's view (`.namedList(...)`); its own list when omitted. */
193
+ list?: string;
192
194
  /** An AIP-160 filter on the list. */
193
195
  filter?: string;
194
196
  /** An AIP-132 order of the list. */
@@ -1089,6 +1091,33 @@ export type ViewModel = {
1089
1091
  create?: CreateModel;
1090
1092
  /** Public forms that create its records for people without an account, each at `/-/forms/<name>`. */
1091
1093
  forms?: FormModel[];
1094
+ /** Other lists of the resource, each with its own sidebar entry and address. */
1095
+ lists?: NamedListModel[];
1096
+ };
1097
+ /**
1098
+ * Another list of a view's resource (`.namedList(...)`), with its own sidebar entry at `/<resource>?list=<name>`: its
1099
+ * own names, list, filter widgets and chart in place of the view's. Its records open at the resource's own address.
1100
+ */
1101
+ export type NamedListModel = {
1102
+ /** Its address, `?list=<name>`: lowercase letters, digits, `-` and `_`, unique in its view. */
1103
+ name: string;
1104
+ /** What its records are called; `plural` is its sidebar entry and title. */
1105
+ names: {
1106
+ /** One record's name, as in "New repair". */
1107
+ singular: string;
1108
+ /** The records' name, as its sidebar entry and title. */
1109
+ plural: string;
1110
+ };
1111
+ /** Where it sits in the sidebar; without `recent`. */
1112
+ nav?: NavModel;
1113
+ /** A line under its title. */
1114
+ help?: string;
1115
+ /** Its list; the resource's first six fields as columns when omitted. */
1116
+ list?: ListModel;
1117
+ /** Its filter widgets, in order. */
1118
+ filters: FilterWidgetModel[];
1119
+ /** Its chart. */
1120
+ chart?: ChartModel;
1092
1121
  };
1093
1122
  /**
1094
1123
  * A public form (`.form(...)`): anyone, signed in or not, opens it at `/-/forms/<name>` and creates a record with its
@@ -0,0 +1,8 @@
1
+ import { ViewModel } from "./model.js";
2
+ //#region src/named-list.d.ts
3
+ /**
4
+ * The view a list page shows: `view` itself, or, with `list`, the view with that named list's names, nav, help, list,
5
+ * filters and chart in place of its own, and no named lists; none when the view has no list by that name.
6
+ */
7
+ export declare const viewOfList: (view: ViewModel | undefined, list: string | undefined) => ViewModel | undefined;
8
+ //#endregion
@@ -0,0 +1,18 @@
1
+ //#region src/named-list.ts
2
+ /**
3
+ * The view a list page shows: `view` itself, or, with `list`, the view with that named list's names, nav, help, list,
4
+ * filters and chart in place of its own, and no named lists; none when the view has no list by that name.
5
+ */
6
+ const viewOfList = (view, list) => {
7
+ if (list === void 0) return view;
8
+ const named = view?.lists?.find((entry) => entry.name === list);
9
+ if (!view || !named) return void 0;
10
+ const { list: _list, chart: _chart, help: _help, nav: _nav, lists: _lists, names: _names, ...rest } = view;
11
+ const { name: _name, ...parts } = named;
12
+ return {
13
+ ...rest,
14
+ ...parts
15
+ };
16
+ };
17
+ //#endregion
18
+ export { viewOfList };
package/dist/view.d.ts CHANGED
@@ -88,6 +88,14 @@ export declare class ViewBuilder<R extends {
88
88
  * `w.toggle` and `w.flags`. Together they write the list's `?filter=`.
89
89
  */
90
90
  filters(build: (r: FieldRefs<FieldsOf<R>>, w: typeof widgets) => FilterWidgetModel[]): ViewBuilder<R>;
91
+ /**
92
+ * Another list of this resource, with its own sidebar entry and address (`/<resource>?list=<name>`). `build` gets a
93
+ * fresh view and sets its `names` (the sidebar entry, required), `nav`, `help`, `list`, `filters` and `chart`, none of
94
+ * which come from the view's own list. Its records open at the resource's own address. Throws on a name that does not
95
+ * fit an address or repeats, on anything else set, on `nav.recent`, and in `toModel` when the view has `listOf` or
96
+ * `searchesOf`. See [More than one list](website/src/content/docs/reference/ui-config.mdx#more-than-one-list).
97
+ */
98
+ namedList(name: string, build: (v: ViewBuilder<R>) => ViewBuilder<R>): ViewBuilder<R>;
91
99
  /**
92
100
  * The record page, as items of `l` in order: sections, sidebar cards, related records, maps and custom components.
93
101
  * Without it the page shows one section with every field.
package/dist/view.js CHANGED
@@ -1,5 +1,7 @@
1
1
  import { createRefs, ref } from "./refs.js";
2
+ import { formNamePattern } from "./form-address.js";
2
3
  import { isMenuHref } from "./menu-href.js";
4
+ import { viewOfList } from "./named-list.js";
3
5
  import { navRecentErrors } from "./nav-recent.js";
4
6
  import { actions } from "./view-actions.js";
5
7
  import { chartModel } from "./view-chart.js";
@@ -9,6 +11,15 @@ import { boardErrors, calendarErrors, displayErrors, displayFields, displays } f
9
11
  import { FieldView } from "./view-field.js";
10
12
  import { widgets } from "./view-widgets.js";
11
13
  //#region src/view.ts
14
+ const namedListParts = /* @__PURE__ */ new Set([
15
+ "resource",
16
+ "names",
17
+ "nav",
18
+ "help",
19
+ "list",
20
+ "filters",
21
+ "chart"
22
+ ]);
12
23
  const searchesOfErrors = (searches, resource) => {
13
24
  const typed = (role, name, type) => name === void 0 || resource.fields[name]?.type === type ? [] : [`${role} "${name}" is not a ${type} field of the resource`];
14
25
  return [
@@ -111,6 +122,34 @@ var ViewBuilder = class ViewBuilder {
111
122
  return this.next({ filters: build(this.refs(), widgets) });
112
123
  }
113
124
  /**
125
+ * Another list of this resource, with its own sidebar entry and address (`/<resource>?list=<name>`). `build` gets a
126
+ * fresh view and sets its `names` (the sidebar entry, required), `nav`, `help`, `list`, `filters` and `chart`, none of
127
+ * which come from the view's own list. Its records open at the resource's own address. Throws on a name that does not
128
+ * fit an address or repeats, on anything else set, on `nav.recent`, and in `toModel` when the view has `listOf` or
129
+ * `searchesOf`. See [More than one list](website/src/content/docs/reference/ui-config.mdx#more-than-one-list).
130
+ */
131
+ namedList(name, build) {
132
+ const where = `View "${this.state.resource}": named list "${name}"`;
133
+ if (!formNamePattern.test(name)) throw new Error(`${where}: its name goes in its address, so it takes lowercase letters, digits, - and _`);
134
+ if (this.state.lists?.some((entry) => entry.name === name)) throw new Error(`${where} is declared twice`);
135
+ const start = view(this.state.resource);
136
+ const { state } = build(start);
137
+ const extra = Object.keys(state).filter((key) => !namedListParts.has(key) && state[key] !== start.state[key]);
138
+ if (extra.length > 0) throw new Error(`${where} sets ${extra.join(", ")}; a named list takes names, nav, help, list, filters and chart`);
139
+ if (!state.names) throw new Error(`${where} needs .names(...), its sidebar entry`);
140
+ if (state.nav?.recent) throw new Error(`${where}: recent records are the view's own, not a named list's`);
141
+ const named = {
142
+ name,
143
+ names: state.names,
144
+ filters: state.filters,
145
+ ...state.nav && { nav: state.nav },
146
+ ...state.help !== void 0 && { help: state.help },
147
+ ...state.list && { list: state.list },
148
+ ...state.chart && { chart: state.chart }
149
+ };
150
+ return this.next({ lists: [...this.state.lists ?? [], named] });
151
+ }
152
+ /**
114
153
  * The record page, as items of `l` in order: sections, sidebar cards, related records, maps and custom components.
115
154
  * Without it the page shows one section with every field.
116
155
  */
@@ -281,6 +320,8 @@ var ViewBuilder = class ViewBuilder {
281
320
  }
282
321
  const recentErrors = resource && nav?.recent ? navRecentErrors(nav.recent, resource) : [];
283
322
  if (recentErrors.length > 0) throw new Error(`View "${this.state.resource}": nav.recent ${recentErrors.join("; ")}`);
323
+ if (this.state.lists?.length && (listOf !== void 0 || this.state.searchesOf)) throw new Error(`View "${this.state.resource}": named lists cannot go with listOf or searchesOf, which replace the list page`);
324
+ for (const named of this.state.lists ?? []) new ViewBuilder(viewOfList(this.state, named.name)).toModel(resource);
284
325
  return structuredClone(this.state);
285
326
  }
286
327
  refs() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@protobase/schema",
3
- "version": "0.22.5",
3
+ "version": "0.22.7",
4
4
  "description": "Resource, field, view and access definitions for Protobase configs.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {