@protobase/schema 0.19.12 → 0.19.13

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/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, CreateModel, CustomSectionModel, DatePreset, DurationLiteral, DurationUnit, EventModel, EventOperation, ExportModel, 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, SectionPresentation, 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, CreateModel, CustomSectionModel, DatePreset, DurationLiteral, DurationUnit, EventModel, EventOperation, ExportModel, 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, RelatedColumnModel, RelatedModel, ResourceModel, SaveFeedback, SaveSearchModel, SearchMatch, SearchResultModel, SearchesOfModel, SectionPresentation, 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";
@@ -9,6 +9,7 @@ import { FieldRefs, FieldsInput, Ref, Refs } from "./refs.js";
9
9
  import { CreatedOptions, DeletedOptions, EventActor, EventContext, EventHelpers, EventMatch, EventSpec, UndeletedOptions, UpdatedOptions } from "./events/event-helpers.js";
10
10
  import { Access, InferRecord, Issue, KeyOf, KeyRefs, RecordKey, RecordOf } from "./resource-types.js";
11
11
  import { ResourceBuilder, SearchRef, SearchRefs, Validator, resource } from "./resource.js";
12
+ import { ListColumn } from "./view-columns.js";
12
13
  import { ComponentRef, ViewProps } from "./view-displays.js";
13
14
  import { FieldView } from "./view-field.js";
14
15
  import { FieldViews, ViewBuilder, view } from "./view.js";
@@ -37,4 +38,4 @@ import { AccessSource, ResolveOptions, ResolvedAccess, RowFilterOperation, acces
37
38
  import { restrictModel } from "./access/restrict.js";
38
39
  import "./access/index.js";
39
40
  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 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 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 SectionPresentation, 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 };
41
+ 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 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 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 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 RecordKey, type RecordMode, type RecordOf, 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 SectionPresentation, 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/model.d.ts CHANGED
@@ -455,6 +455,31 @@ export type ListModel = {
455
455
  displays?: ListDisplayModel[];
456
456
  /** The display the list opens in when the address names none; the table when omitted. */
457
457
  default?: string;
458
+ /** The columns of related records among `columns`, each named there by its `name`. */
459
+ relatedColumns?: RelatedColumnModel[];
460
+ };
461
+ /**
462
+ * A list column of the records of another resource that point at each row, directly or through a join resource, as a
463
+ * dot per record in its status's colour, each opening the record (`c.related(...)`). See
464
+ * [Related records in a list](website/src/content/docs/reference/ui-config.mdx#related-records-in-a-list).
465
+ */
466
+ export type RelatedColumnModel = {
467
+ /** The column's name in the list's `columns`, which no field of the resource has. */
468
+ name: string;
469
+ /** The column's header; the name in words when omitted. */
470
+ label?: string;
471
+ /** The resource whose records it shows. */
472
+ resource: string;
473
+ /** The filterable field of `resource` matched against the row's key, or with `through` against the join records' keys. */
474
+ field: string;
475
+ /** The join records between the row and the shown ones, as a related section's. */
476
+ through?: RelatedModel['through'];
477
+ /** An AIP-160 filter on the shown records. */
478
+ filter?: string;
479
+ /** An AIP-132 order of the dots. */
480
+ sort?: string;
481
+ /** The enum field of `resource` whose value colours each dot, by its view's `valueColors`. */
482
+ status: string;
458
483
  };
459
484
  /** A button of a date range filter: today, yesterday, the last 7, 30 or 90 days, or this month, quarter or year. */
460
485
  export type DatePreset = 'today' | 'yesterday' | '7d' | '30d' | '90d' | 'month' | 'quarter' | 'year';
@@ -0,0 +1,18 @@
1
+ import { RelatedColumnModel } from "./model.js";
2
+ //#region src/view-columns.d.ts
3
+ /** A column of a view's list that is no field of the resource, as `c` makes one. */
4
+ export type ListColumn = RelatedColumnModel & {
5
+ /** Marks a related records column. */
6
+ readonly kind: 'related';
7
+ };
8
+ export declare const columns: {
9
+ /**
10
+ * A column named `name` of the records of `resource` that point at each row, through its relation `field` or, with
11
+ * `through`, through a join resource, as a related section's. Each record is a dot in the colour its `status` value
12
+ * has in its view's `valueColors`, in `sort` order, and a link to the record, showing its card on hover or focus. The
13
+ * name must not be a field of the resource. See
14
+ * [Related records in a list](website/src/content/docs/reference/ui-config.mdx#related-records-in-a-list).
15
+ */
16
+ related: (name: string, o: Omit<RelatedColumnModel, "name">) => ListColumn;
17
+ };
18
+ //#endregion
@@ -0,0 +1,17 @@
1
+ //#region src/view-columns.ts
2
+ const isListColumn = (value) => typeof value === "object" && value !== null && value.kind === "related";
3
+ const columns = {
4
+ /**
5
+ * A column named `name` of the records of `resource` that point at each row, through its relation `field` or, with
6
+ * `through`, through a join resource, as a related section's. Each record is a dot in the colour its `status` value
7
+ * has in its view's `valueColors`, in `sort` order, and a link to the record, showing its card on hover or focus. The
8
+ * name must not be a field of the resource. See
9
+ * [Related records in a list](website/src/content/docs/reference/ui-config.mdx#related-records-in-a-list).
10
+ */
11
+ related: (name, o) => ({
12
+ kind: "related",
13
+ name,
14
+ ...structuredClone(o)
15
+ }) };
16
+ //#endregion
17
+ export { columns, isListColumn };
package/dist/view.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { ActionModel, ChartGranularity, ChartRange, CreateModel, ExportModel, 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
+ import { ListColumn, columns } from "./view-columns.js";
4
5
  import { displays } from "./view-displays.js";
5
6
  import { FieldView } from "./view-field.js";
6
7
  import { widgets } from "./view-widgets.js";
@@ -65,11 +66,12 @@ export declare class ViewBuilder<R extends {
65
66
  /** Hints per field, such as `{ total: r.total.prefix('€').decimals(2) }`; calls add up. */
66
67
  fields(build: (r: FieldViews<R>) => Record<string, FieldView>): ViewBuilder<R>;
67
68
  /**
68
- * The list: its table's `columns`, `sort` and `search`, and other `displays` to switch to (`d.cards`, `d.custom`), one
69
- * of which may be the `default` the list opens in. Throws when display names collide or `default` names none.
69
+ * The list: its table's `columns`, fields or columns of related records (`c.related`), `sort` and `search`, and other
70
+ * `displays` to switch to (`d.cards`, `d.custom`), one of which may be the `default` the list opens in. Throws when
71
+ * display or column names collide or `default` names none.
70
72
  */
71
- list(build: (r: FieldRefs<FieldsOf<R>>, d: typeof displays) => {
72
- columns: Ref[];
73
+ list(build: (r: FieldRefs<FieldsOf<R>>, d: typeof displays, c: typeof columns) => {
74
+ columns: Array<Ref | ListColumn>;
73
75
  sort?: Array<[Ref, 'asc' | 'desc']>;
74
76
  search?: Ref[];
75
77
  displays?: ListDisplayModel[];
@@ -172,7 +174,7 @@ export declare class ViewBuilder<R extends {
172
174
  };
173
175
  limit?: number;
174
176
  }): ViewBuilder<R>;
175
- /** 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. */
177
+ /** With the resource's model, also verifies the title, search result and list display fields, related column names, a gallery's image, `listOf`, `searchesOf` and the sidebar's `recent` group. */
176
178
  toModel(resource?: ResourceModel): ViewModel;
177
179
  private refs;
178
180
  private next;
package/dist/view.js CHANGED
@@ -2,6 +2,7 @@ import { createRefs, ref } from "./refs.js";
2
2
  import { isMenuHref } from "./menu-href.js";
3
3
  import { navRecentErrors } from "./nav-recent.js";
4
4
  import { actions } from "./view-actions.js";
5
+ import { columns, isListColumn } from "./view-columns.js";
5
6
  import { displayErrors, displayFields, displays } from "./view-displays.js";
6
7
  import { FieldView } from "./view-field.js";
7
8
  import { widgets } from "./view-widgets.js";
@@ -72,13 +73,19 @@ var ViewBuilder = class ViewBuilder {
72
73
  } });
73
74
  }
74
75
  /**
75
- * The list: its table's `columns`, `sort` and `search`, and other `displays` to switch to (`d.cards`, `d.custom`), one
76
- * of which may be the `default` the list opens in. Throws when display names collide or `default` names none.
76
+ * The list: its table's `columns`, fields or columns of related records (`c.related`), `sort` and `search`, and other
77
+ * `displays` to switch to (`d.cards`, `d.custom`), one of which may be the `default` the list opens in. Throws when
78
+ * display or column names collide or `default` names none.
77
79
  */
78
80
  list(build) {
79
- const { columns, sort, search, displays: shown = [], default: opens } = build(this.refs(), displays);
81
+ const { columns: columns$1, sort, search, displays: shown = [], default: opens } = build(this.refs(), displays, columns);
82
+ const related = columns$1.filter(isListColumn).map(({ kind: _, ...column }) => column);
83
+ const names = columns$1.map((column) => column.name);
84
+ const repeated = names.find((name, index) => names.indexOf(name) !== index);
85
+ if (repeated !== void 0) throw new Error(`View "${this.state.resource}": the list has two columns named "${repeated}"`);
80
86
  const list = {
81
- columns: columns.map((ref) => ref.name),
87
+ columns: names,
88
+ ...related.length > 0 && { relatedColumns: related },
82
89
  ...sort && { sort: sort.map(([ref, dir]) => [ref.name, dir]) },
83
90
  ...search && { search: search.map((ref) => ref.name) },
84
91
  ...shown.length > 0 && { displays: shown },
@@ -218,7 +225,7 @@ var ViewBuilder = class ViewBuilder {
218
225
  };
219
226
  return this.next({ searchesOf: searches });
220
227
  }
221
- /** 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. */
228
+ /** With the resource's model, also verifies the title, search result and list display fields, related column names, a gallery's image, `listOf`, `searchesOf` and the sidebar's `recent` group. */
222
229
  toModel(resource) {
223
230
  const { title, nav, searchResult, list, listOf } = this.state;
224
231
  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`);
@@ -231,6 +238,8 @@ var ViewBuilder = class ViewBuilder {
231
238
  const flag = display.kind === "cards" ? display.flags?.find((name) => resource.fields[name].type !== "boolean") : void 0;
232
239
  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`);
233
240
  }
241
+ const clash = resource ? list?.relatedColumns?.find((column) => Object.hasOwn(resource.fields, column.name)) : void 0;
242
+ if (clash) throw new Error(`View "${this.state.resource}": related column "${clash.name}" has the name of a field of the resource`);
234
243
  for (const item of resource ? this.state.layout : []) {
235
244
  if (item.kind !== "gallery" || item.image === void 0) continue;
236
245
  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`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@protobase/schema",
3
- "version": "0.19.12",
3
+ "version": "0.19.13",
4
4
  "description": "Resource, field, view and access definitions for Protobase configs.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {