@protobase/schema 0.19.15 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +4 -3
- package/dist/index.js +2 -2
- package/dist/internal.d.ts +3 -2
- package/dist/internal.js +3 -2
- package/dist/keys.d.ts +2 -0
- package/dist/keys.js +10 -1
- package/dist/model.d.ts +43 -2
- package/dist/view-actions.d.ts +4 -1
- package/dist/view-actions.js +4 -1
- package/dist/view-chart.d.ts +32 -0
- package/dist/view-chart.js +29 -0
- package/dist/view-displays.d.ts +28 -2
- package/dist/view-displays.js +41 -2
- package/dist/view.d.ts +14 -10
- package/dist/view.js +19 -12
- package/package.json +1 -1
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, 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, ShowWhen, SignInPromptModel, SignInViewModel, SortSpec, Span, StatusTone, UserMenuItemModel, UserMenuModel, ViewModel } from "./model.js";
|
|
2
|
+
import { ActionInputKind, ActionInputModel, 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, GroupChartModel, HelpWhen, JsonValue, KeyValue, LayoutItemModel, ListDisplayModel, ListModel, ListQuery, Literal, MapSectionModel, MapView, NavModel, NavRecentModel, NowValue, OrderByItem, PeriodChartModel, RecordMode, RelatedColumnModel, RelatedModel, ResourceModel, SaveFeedback, SaveSearchModel, SearchMatch, SearchResultModel, SearchesOfModel, 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";
|
|
@@ -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 { ChartInput, GroupChartInput, PeriodChartInput } from "./view-chart.js";
|
|
12
13
|
import { ListColumn } from "./view-columns.js";
|
|
13
14
|
import { ComponentRef, ViewProps } from "./view-displays.js";
|
|
14
15
|
import { FieldView } from "./view-field.js";
|
|
@@ -19,7 +20,7 @@ import { UserMenuSource, userMenu } from "./user-menu.js";
|
|
|
19
20
|
import { BottomBarSource, bottomBar, maxBottomBarItems } from "./bottom-bar.js";
|
|
20
21
|
import { BottomBarNames, bottomBarErrors } from "./bottom-bar-errors.js";
|
|
21
22
|
import { layout } from "./layout.js";
|
|
22
|
-
import { KeyType, keyTypes } from "./keys.js";
|
|
23
|
+
import { KeyType, fitsKeyType, keyTypes } from "./keys.js";
|
|
23
24
|
import { addressWords, appPath, readAppPath } from "./app-address.js";
|
|
24
25
|
import { SignInAddress, SignInStep, readSignInAddress, signInParams, signInPath, signInRoute, signInSteps } from "./sign-in-address.js";
|
|
25
26
|
import { FilterLimits, FilterParse, parseFilter } from "./filter/parse.js";
|
|
@@ -38,4 +39,4 @@ import { AccessSource, ResolveOptions, ResolvedAccess, RowFilterOperation, acces
|
|
|
38
39
|
import { restrictModel } from "./access/restrict.js";
|
|
39
40
|
import "./access/index.js";
|
|
40
41
|
import { AssistantAction, AssistantActionRequest, AssistantCardPart, AssistantDiff, AssistantEvent, AssistantMessage, AssistantMessageRequest, AssistantPart, AssistantState, AssistantStep, AssistantStepState, AssistantTablePart, AssistantTextPart, AssistantTone, AssistantWidgetPart } from "./assistant/protocol.js";
|
|
41
|
-
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 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 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, imageFormats, keyTypes, layout as l, maxBottomBarItems, parseFilter, parseOrderBy, pickView, printFilter, printOrderBy, readAppPath, readSignInAddress, resolveAccess, resource, restrictModel, rule, signInParams, signInPath, signInRoute, signInSteps, userMenu, view, where };
|
|
42
|
+
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 Capability, type CapabilityAction, type CapabilityLimit, type CapabilityScope, type CardFavoriteModel, type CardPhotosModel, type ChartGranularity, type ChartInput, 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 GroupChartInput, type GroupChartModel, 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 PeriodChartInput, type PeriodChartModel, 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 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, 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/index.js
CHANGED
|
@@ -16,11 +16,11 @@ import { userMenu } from "./user-menu.js";
|
|
|
16
16
|
import { bottomBar, maxBottomBarItems } from "./bottom-bar.js";
|
|
17
17
|
import { bottomBarErrors } from "./bottom-bar-errors.js";
|
|
18
18
|
import { layout } from "./layout.js";
|
|
19
|
-
import { keyTypes } from "./keys.js";
|
|
19
|
+
import { fitsKeyType, keyTypes } from "./keys.js";
|
|
20
20
|
import { addressWords, appPath, readAppPath } from "./app-address.js";
|
|
21
21
|
import { readSignInAddress, signInParams, signInPath, signInRoute, signInSteps } from "./sign-in-address.js";
|
|
22
22
|
import { rule } from "./access/rule.js";
|
|
23
23
|
import { defineRoles } from "./access/roles.js";
|
|
24
24
|
import { pickView } from "./access/pick-view.js";
|
|
25
25
|
import { restrictModel } from "./access/restrict.js";
|
|
26
|
-
export { 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 };
|
|
26
|
+
export { accessWarnings, addressWords, anonymousRole, appPath, bottomBar, bottomBarErrors, checkFilter, checkOrderBy, checkRecord, createWhere, defineRoles, enumLabel, evaluateFilter, f, fieldRef, fieldsUsedBy, fitsKeyType, 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/internal.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { checkedAccept, checkedProvider, parseSize } from "./files/file-options.js";
|
|
2
2
|
import { actionInputErrors, shownInputs } from "./action-inputs.js";
|
|
3
|
-
import {
|
|
3
|
+
import { chartFields, isGroupChart } from "./view-chart.js";
|
|
4
|
+
import { boardErrors, cardPhotoLimit, displayFields, tableDisplay } from "./view-displays.js";
|
|
4
5
|
import { fieldRefsIn, isFieldRef, markFieldRefs, unmarkFieldRefs } from "./field-ref.js";
|
|
5
6
|
import { bottomBarFor } from "./bottom-bar.js";
|
|
6
7
|
import { decodeKey, encodeKey } from "./keys.js";
|
|
@@ -15,4 +16,4 @@ import { addressPlaceholders, appHrefError, fillAddress, hrefField, hrefPlacehol
|
|
|
15
16
|
import { AddressCheck, addressErrors } from "./address-errors.js";
|
|
16
17
|
import { applyAssistantEvent, emptyAssistantState } from "./assistant/apply-event.js";
|
|
17
18
|
import { readEventStream } from "./assistant/event-stream.js";
|
|
18
|
-
export { type AddressCheck, type PluginHost, type PluginRequirement, type PropShapes, type RefShape, type SearchTerm, actionInputErrors, addressErrors, addressPlaceholders, appHrefError, applyAssistantEvent, bottomBarFor, cardPhotoLimit, checkEventFilter, checkedAccept, checkedProvider, decodeKey, defaultRecentLimit, displayFields, emptyAssistantState, encodeKey, fieldRefsIn, fillAddress, hrefField, hrefPlaceholders, inRange, isFieldRef, leavesApp, linkActionError, markFieldRefs, matchedSearchField, matchesSearch, navRecentErrors, parseSize, pluginProblem, pruneByShape, readEventStream, redactSecrets, searchTerms, shapes, shownInputs, statusTones, tableDisplay, termMatches, unmarkFieldRefs, webhookResources };
|
|
19
|
+
export { type AddressCheck, type PluginHost, type PluginRequirement, type PropShapes, type RefShape, type SearchTerm, actionInputErrors, addressErrors, addressPlaceholders, appHrefError, applyAssistantEvent, boardErrors, bottomBarFor, cardPhotoLimit, chartFields, checkEventFilter, checkedAccept, checkedProvider, decodeKey, defaultRecentLimit, displayFields, emptyAssistantState, encodeKey, fieldRefsIn, fillAddress, hrefField, hrefPlaceholders, inRange, isFieldRef, isGroupChart, leavesApp, linkActionError, markFieldRefs, matchedSearchField, matchesSearch, navRecentErrors, parseSize, pluginProblem, pruneByShape, readEventStream, redactSecrets, searchTerms, shapes, shownInputs, statusTones, tableDisplay, termMatches, unmarkFieldRefs, webhookResources };
|
package/dist/internal.js
CHANGED
|
@@ -4,8 +4,9 @@ import { checkEventFilter } from "./events/event-models.js";
|
|
|
4
4
|
import { defaultRecentLimit, navRecentErrors, statusTones } from "./nav-recent.js";
|
|
5
5
|
import { actionInputErrors, shownInputs } from "./action-inputs.js";
|
|
6
6
|
import { addressPlaceholders, appHrefError, fillAddress, hrefField, hrefPlaceholders, leavesApp, linkActionError } from "./address-template.js";
|
|
7
|
+
import { chartFields, isGroupChart } from "./view-chart.js";
|
|
7
8
|
import { fieldRefsIn, isFieldRef, markFieldRefs, unmarkFieldRefs } from "./field-ref.js";
|
|
8
|
-
import { cardPhotoLimit, displayFields, tableDisplay } from "./view-displays.js";
|
|
9
|
+
import { boardErrors, cardPhotoLimit, displayFields, tableDisplay } from "./view-displays.js";
|
|
9
10
|
import { bottomBarFor } from "./bottom-bar.js";
|
|
10
11
|
import { decodeKey, encodeKey } from "./keys.js";
|
|
11
12
|
import { webhookResources } from "./webhooks/webhook-resources.js";
|
|
@@ -15,4 +16,4 @@ import { redactSecrets } from "./redact-secrets.js";
|
|
|
15
16
|
import { addressErrors } from "./address-errors.js";
|
|
16
17
|
import { applyAssistantEvent, emptyAssistantState } from "./assistant/apply-event.js";
|
|
17
18
|
import { readEventStream } from "./assistant/event-stream.js";
|
|
18
|
-
export { actionInputErrors, addressErrors, addressPlaceholders, appHrefError, applyAssistantEvent, bottomBarFor, cardPhotoLimit, checkEventFilter, checkedAccept, checkedProvider, decodeKey, defaultRecentLimit, displayFields, emptyAssistantState, encodeKey, fieldRefsIn, fillAddress, hrefField, hrefPlaceholders, inRange, isFieldRef, leavesApp, linkActionError, markFieldRefs, matchedSearchField, matchesSearch, navRecentErrors, parseSize, pluginProblem, pruneByShape, readEventStream, redactSecrets, searchTerms, shapes, shownInputs, statusTones, tableDisplay, termMatches, unmarkFieldRefs, webhookResources };
|
|
19
|
+
export { actionInputErrors, addressErrors, addressPlaceholders, appHrefError, applyAssistantEvent, boardErrors, bottomBarFor, cardPhotoLimit, chartFields, checkEventFilter, checkedAccept, checkedProvider, decodeKey, defaultRecentLimit, displayFields, emptyAssistantState, encodeKey, fieldRefsIn, fillAddress, hrefField, hrefPlaceholders, inRange, isFieldRef, isGroupChart, leavesApp, linkActionError, markFieldRefs, matchedSearchField, matchesSearch, navRecentErrors, parseSize, pluginProblem, pruneByShape, readEventStream, redactSecrets, searchTerms, shapes, shownInputs, statusTones, tableDisplay, termMatches, unmarkFieldRefs, webhookResources };
|
package/dist/keys.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ import { FieldType, KeyValue, ResourceModel } from "./model.js";
|
|
|
2
2
|
//#region src/keys.d.ts
|
|
3
3
|
/** The field types a primary key part may have: `integer`, `bigint`, `uuid` or `text`. */
|
|
4
4
|
export type KeyType = Extract<FieldType, 'integer' | 'bigint' | 'uuid' | 'text'>;
|
|
5
|
+
/** Whether a column of `type` can hold `value`, such as a user id an owner field stores. */
|
|
6
|
+
export declare const fitsKeyType: (value: string, type: KeyType) => boolean;
|
|
5
7
|
export declare const encodeKey: (key: KeyValue | readonly KeyValue[]) => string;
|
|
6
8
|
export declare const decodeKey: (encoded: string, types: readonly KeyType[]) => string | number | (string | number)[];
|
|
7
9
|
/**
|
package/dist/keys.js
CHANGED
|
@@ -13,6 +13,15 @@ const decodePart = (part, type) => {
|
|
|
13
13
|
if (!Number.isSafeInteger(value)) throw new Error("Invalid integer key part");
|
|
14
14
|
return value;
|
|
15
15
|
};
|
|
16
|
+
/** Whether a column of `type` can hold `value`, such as a user id an owner field stores. */
|
|
17
|
+
const fitsKeyType = (value, type) => {
|
|
18
|
+
if (type === "text") return true;
|
|
19
|
+
if (type === "uuid") return uuidPattern.test(value);
|
|
20
|
+
if (!integerPattern.test(value)) return false;
|
|
21
|
+
if (type === "bigint") return true;
|
|
22
|
+
const number = Number(value);
|
|
23
|
+
return Number.isSafeInteger(number) && Math.abs(number) <= 2 ** 31 - 1;
|
|
24
|
+
};
|
|
16
25
|
const decodeComponent = (part) => {
|
|
17
26
|
try {
|
|
18
27
|
return decodeURIComponent(part);
|
|
@@ -54,4 +63,4 @@ const keyTypes = (model, resolve, seen = []) => model.primaryKey.flatMap((name)
|
|
|
54
63
|
return types;
|
|
55
64
|
});
|
|
56
65
|
//#endregion
|
|
57
|
-
export { decodeKey, encodeKey, keyTypes };
|
|
66
|
+
export { decodeKey, encodeKey, fitsKeyType, keyTypes };
|
package/dist/model.d.ts
CHANGED
|
@@ -304,6 +304,8 @@ export type ListDisplayModel = {
|
|
|
304
304
|
footer?: string[];
|
|
305
305
|
/** Boolean fields shown as small badges of their labels when true. */
|
|
306
306
|
flags?: string[];
|
|
307
|
+
/** Related columns of the list (`c.related`) shown as lines of their dots at the card's bottom, by their names. */
|
|
308
|
+
dots?: string[];
|
|
307
309
|
/** The component that draws each card's content instead of the built-in card. */
|
|
308
310
|
card?: string;
|
|
309
311
|
/** Where the card's further photos come from. */
|
|
@@ -335,6 +337,25 @@ export type ListDisplayModel = {
|
|
|
335
337
|
defaultRadius?: number;
|
|
336
338
|
/** The field holding the GeoJSON geometry around an approximate position: a polygon, or a line such as a street. */
|
|
337
339
|
area?: string;
|
|
340
|
+
} | {
|
|
341
|
+
/** A column per value of an enum field, a card per record in its value's column, moved by dragging. */
|
|
342
|
+
kind: 'board';
|
|
343
|
+
/** What the address names (`?display=board`). */
|
|
344
|
+
name: string;
|
|
345
|
+
/** The display's name in the switch. */
|
|
346
|
+
label?: string;
|
|
347
|
+
/** The display's icon in the switch. */
|
|
348
|
+
icon?: string;
|
|
349
|
+
/** The filterable enum field whose values are the columns, and which a card dragged to another column sets. */
|
|
350
|
+
by: string;
|
|
351
|
+
/** The card's title; the view's title when omitted. */
|
|
352
|
+
title?: string;
|
|
353
|
+
/** The line under the title. */
|
|
354
|
+
subtitle?: string;
|
|
355
|
+
/** A field drawn as a badge with its value labels. */
|
|
356
|
+
badge?: string;
|
|
357
|
+
/** A few small fields, such as dates, each shown when it has a value. */
|
|
358
|
+
meta?: string[];
|
|
338
359
|
} | {
|
|
339
360
|
/** The rows drawn by a project component. */
|
|
340
361
|
kind: 'custom';
|
|
@@ -465,6 +486,11 @@ export type ListModel = {
|
|
|
465
486
|
default?: string;
|
|
466
487
|
/** The columns of related records among `columns`, each named there by its `name`. */
|
|
467
488
|
relatedColumns?: RelatedColumnModel[];
|
|
489
|
+
/**
|
|
490
|
+
* The fields among `columns` edited in their cell, by a user who may change the record and the field; see
|
|
491
|
+
* [Editing in the list](website/src/content/docs/reference/ui-config.mdx#editing-in-the-list).
|
|
492
|
+
*/
|
|
493
|
+
editable?: string[];
|
|
468
494
|
};
|
|
469
495
|
/**
|
|
470
496
|
* A list column of the records of another resource that point at each row, directly or through a join resource, as a
|
|
@@ -661,8 +687,8 @@ export type GalleryModel = {
|
|
|
661
687
|
export type ChartRange = '7d' | '30d' | '90d' | '1y' | 'all';
|
|
662
688
|
/** How long each bar of a list's chart is. */
|
|
663
689
|
export type ChartGranularity = 'hour' | 'day' | 'week' | 'month';
|
|
664
|
-
/** A chart of records over time above a list, following its filters (`.chart(...)`). */
|
|
665
|
-
export type
|
|
690
|
+
/** A chart of records over time above a list, following its filters (`.chart(...)` with `field`). */
|
|
691
|
+
export type PeriodChartModel = {
|
|
666
692
|
/** The date or timestamp field the records are counted by. */
|
|
667
693
|
field: string;
|
|
668
694
|
/** How far back it reaches. */
|
|
@@ -670,6 +696,21 @@ export type ChartModel = {
|
|
|
670
696
|
/** How long each bar is. */
|
|
671
697
|
granularity: ChartGranularity;
|
|
672
698
|
};
|
|
699
|
+
/** A chart of records per value of a field above a list, following its filters (`.chart(...)` with `groupBy`). */
|
|
700
|
+
export type GroupChartModel = {
|
|
701
|
+
/** The field whose values make the groups, such as a status or a relation; not a date. */
|
|
702
|
+
groupBy: string;
|
|
703
|
+
/** Bars, the largest first, or a donut. */
|
|
704
|
+
type: 'bar' | 'donut';
|
|
705
|
+
/** A numeric field each group adds up, instead of counting its records. */
|
|
706
|
+
sum?: string;
|
|
707
|
+
/** A numeric field each group averages, instead of counting its records. */
|
|
708
|
+
average?: string;
|
|
709
|
+
/** How many values it shows, the largest. */
|
|
710
|
+
limit: number;
|
|
711
|
+
};
|
|
712
|
+
/** The chart above a list: records over time, or records per value of a field. */
|
|
713
|
+
export type ChartModel = PeriodChartModel | GroupChartModel;
|
|
673
714
|
/**
|
|
674
715
|
* How the record page opens to a user who may change the record: `edit`, the default, as its form, ready to change;
|
|
675
716
|
* `read` as a read view of label/value tables, with an Edit button to the form; `locked` as the same form, locked until
|
package/dist/view-actions.d.ts
CHANGED
|
@@ -17,7 +17,10 @@ export declare const actions: {
|
|
|
17
17
|
bulk?: boolean;
|
|
18
18
|
inputs?: (i: typeof inputs) => ActionInputModel[];
|
|
19
19
|
}) => ActionModel;
|
|
20
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* Sets fields of the record, as one update with its ETag, for example `{ status: 'paid' }`. It is not offered on a
|
|
22
|
+
* record that holds these values already.
|
|
23
|
+
*/
|
|
21
24
|
update: (name: string, o: ActionOptions & {
|
|
22
25
|
set: Record<string, unknown>;
|
|
23
26
|
}) => ActionModel;
|
package/dist/view-actions.js
CHANGED
|
@@ -24,7 +24,10 @@ const actions = {
|
|
|
24
24
|
inputs: built
|
|
25
25
|
};
|
|
26
26
|
},
|
|
27
|
-
/**
|
|
27
|
+
/**
|
|
28
|
+
* Sets fields of the record, as one update with its ETag, for example `{ status: 'paid' }`. It is not offered on a
|
|
29
|
+
* record that holds these values already.
|
|
30
|
+
*/
|
|
28
31
|
update: (name, o) => ({
|
|
29
32
|
...named(name, o),
|
|
30
33
|
run: {
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { ChartGranularity, ChartModel, ChartRange, GroupChartModel } from "./model.js";
|
|
2
|
+
import { Ref } from "./refs.js";
|
|
3
|
+
//#region src/view-chart.d.ts
|
|
4
|
+
/** Records per `granularity` over a date or timestamp `field`. */
|
|
5
|
+
export type PeriodChartInput = {
|
|
6
|
+
/** The date or timestamp field the records are counted by. */
|
|
7
|
+
field: Ref;
|
|
8
|
+
/** How far back it reaches. */
|
|
9
|
+
range: ChartRange;
|
|
10
|
+
/** How long each bar is. */
|
|
11
|
+
granularity: ChartGranularity;
|
|
12
|
+
};
|
|
13
|
+
/** Records per value of `groupBy`, counted, or with `sum` or `average` that of a numeric field. */
|
|
14
|
+
export type GroupChartInput = {
|
|
15
|
+
/** The field whose values make the groups, such as a status or a relation; not a date. */
|
|
16
|
+
groupBy: Ref;
|
|
17
|
+
/** Bars, the default, or a donut. */
|
|
18
|
+
type?: GroupChartModel['type'];
|
|
19
|
+
/** How many values it shows, the largest; default 10. */
|
|
20
|
+
limit?: number;
|
|
21
|
+
/** A numeric field each group adds up, instead of counting its records. */
|
|
22
|
+
sum?: Ref;
|
|
23
|
+
/** A numeric field each group averages, instead of counting its records. */
|
|
24
|
+
average?: Ref;
|
|
25
|
+
};
|
|
26
|
+
/** What `.chart(...)` takes: a chart over time or per value. */
|
|
27
|
+
export type ChartInput = PeriodChartInput | GroupChartInput;
|
|
28
|
+
/** Whether a list's chart counts records per value of a field rather than over time. */
|
|
29
|
+
export declare const isGroupChart: (chart: ChartModel) => chart is GroupChartModel;
|
|
30
|
+
/** The fields a list's chart reads: its date field, or the field it groups by and the one it adds up. */
|
|
31
|
+
export declare const chartFields: (chart: ChartModel) => string[];
|
|
32
|
+
//#endregion
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
//#region src/view-chart.ts
|
|
2
|
+
const defaultLimit = 10;
|
|
3
|
+
const chartModel = (input) => {
|
|
4
|
+
if (!("groupBy" in input)) return {
|
|
5
|
+
field: input.field.name,
|
|
6
|
+
range: input.range,
|
|
7
|
+
granularity: input.granularity
|
|
8
|
+
};
|
|
9
|
+
if (input.sum && input.average) throw new Error("A chart takes sum or average, not both");
|
|
10
|
+
const limit = input.limit ?? defaultLimit;
|
|
11
|
+
if (!Number.isInteger(limit) || limit < 1) throw new Error(`A chart's limit must be a positive whole number, not ${limit}`);
|
|
12
|
+
return {
|
|
13
|
+
groupBy: input.groupBy.name,
|
|
14
|
+
type: input.type ?? "bar",
|
|
15
|
+
...input.sum && { sum: input.sum.name },
|
|
16
|
+
...input.average && { average: input.average.name },
|
|
17
|
+
limit
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
/** Whether a list's chart counts records per value of a field rather than over time. */
|
|
21
|
+
const isGroupChart = (chart) => "groupBy" in chart;
|
|
22
|
+
/** The fields a list's chart reads: its date field, or the field it groups by and the one it adds up. */
|
|
23
|
+
const chartFields = (chart) => isGroupChart(chart) ? [
|
|
24
|
+
chart.groupBy,
|
|
25
|
+
...chart.sum ? [chart.sum] : [],
|
|
26
|
+
...chart.average ? [chart.average] : []
|
|
27
|
+
] : [chart.field];
|
|
28
|
+
//#endregion
|
|
29
|
+
export { chartFields, chartModel, isGroupChart };
|
package/dist/view-displays.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { CardFavoriteModel, CardPhotosModel, ListDisplayModel } from "./model.js";
|
|
1
|
+
import { CardFavoriteModel, CardPhotosModel, ListDisplayModel, ResourceModel } from "./model.js";
|
|
2
2
|
import { Ref } from "./refs.js";
|
|
3
|
+
import { ListColumn } from "./view-columns.js";
|
|
3
4
|
//#region src/view-displays.d.ts
|
|
4
5
|
/** A custom component as `component(name)` from `@protobase/layout` declares it. */
|
|
5
6
|
export type ComponentRef<P> = ((props: P) => unknown) & {
|
|
@@ -26,6 +27,8 @@ export declare const displays: {
|
|
|
26
27
|
* view's title) over `subtitle`, `badge` drawn as a badge with its value labels, `amount` the first of its fields with a
|
|
27
28
|
* value in bold, such as a purchase price or a rent, and a few small `meta` fields such as dates. `footer` is a line at
|
|
28
29
|
* the card's bottom of its fields with a value, joined by bullets. `flags` are boolean fields shown as small badges of their labels when true, such as a garden or parking.
|
|
30
|
+
* `dots` are related columns of the list (`c.related`), each a line of its dots under the rest of the card, with the
|
|
31
|
+
* colours, order and hover cards they have in the table; a column with no records for the row shows no line.
|
|
29
32
|
* With `card`, a component of the project or a plugin draws each card's content instead, from the row of those
|
|
30
33
|
* fields (`{ row }`); without it, or when it throws, the card is the built-in one. With `photos`, the built-in card's
|
|
31
34
|
* picture gets arrows through the `image` of the records of another resource that point at the card's record, after
|
|
@@ -43,6 +46,7 @@ export declare const displays: {
|
|
|
43
46
|
meta?: Ref[];
|
|
44
47
|
footer?: Ref[];
|
|
45
48
|
flags?: Ref[];
|
|
49
|
+
dots?: ListColumn[];
|
|
46
50
|
}, o?: Naming & {
|
|
47
51
|
card?: ComponentRef<never>;
|
|
48
52
|
photos?: CardPhotosModel;
|
|
@@ -70,6 +74,24 @@ export declare const displays: {
|
|
|
70
74
|
cards?: string;
|
|
71
75
|
defaultRadius?: number;
|
|
72
76
|
}) => ListDisplayModel;
|
|
77
|
+
/**
|
|
78
|
+
* A column per value of the enum field `by`, in the order the field lists them, each headed by its value label and how
|
|
79
|
+
* many of the list's records have it; with a column for records without a value when the field may be empty. Each
|
|
80
|
+
* column pages through the list API with the list's filter and search, its value added, in the list's order. A card
|
|
81
|
+
* shows `title` (default: the view's title) over `subtitle`, with `badge` and a few small `meta` fields, as the built-in
|
|
82
|
+
* card of `d.cards` does, and opens the record. Dragging a card to another column, or picking it up with Space and
|
|
83
|
+
* moving it with the arrow keys, saves the record with `by` set to that column's value, as one update with its ETag,
|
|
84
|
+
* so the resource's access rules and `validate` decide, and a refusal puts the card back and says why. `by` must be
|
|
85
|
+
* a filterable enum field. Named `board` unless `name` says otherwise. See
|
|
86
|
+
* [Boards](website/src/content/docs/reference/ui-config.mdx#boards).
|
|
87
|
+
*/
|
|
88
|
+
board: (fields: {
|
|
89
|
+
by: Ref;
|
|
90
|
+
title?: Ref;
|
|
91
|
+
subtitle?: Ref;
|
|
92
|
+
badge?: Ref;
|
|
93
|
+
meta?: Ref[];
|
|
94
|
+
}, o?: Naming) => ListDisplayModel;
|
|
73
95
|
/**
|
|
74
96
|
* The list drawn by a component of the project, registered in `protobase.ui.tsx` under its name. It gets `props` and
|
|
75
97
|
* reads the rows with `useListDisplay()`; `fields` are the fields it shows, fetched with relation names.
|
|
@@ -81,6 +103,10 @@ export declare const displays: {
|
|
|
81
103
|
props?: ViewProps<P>;
|
|
82
104
|
}) => ListDisplayModel;
|
|
83
105
|
};
|
|
84
|
-
/**
|
|
106
|
+
/** What makes a board unable to put the records in columns: a `by` field that is no filterable enum. */
|
|
107
|
+
export declare const boardErrors: (display: Extract<ListDisplayModel, {
|
|
108
|
+
kind: "board";
|
|
109
|
+
}>, model: ResourceModel) => string[];
|
|
110
|
+
/** Every field a display names: what it shows, a map's position and the fields saying how exact it is, a board's columns, and the field references in a custom display's props. */
|
|
85
111
|
export declare const displayFields: (display: ListDisplayModel) => string[];
|
|
86
112
|
//#endregion
|
package/dist/view-displays.js
CHANGED
|
@@ -19,6 +19,8 @@ const displays = {
|
|
|
19
19
|
* view's title) over `subtitle`, `badge` drawn as a badge with its value labels, `amount` the first of its fields with a
|
|
20
20
|
* value in bold, such as a purchase price or a rent, and a few small `meta` fields such as dates. `footer` is a line at
|
|
21
21
|
* the card's bottom of its fields with a value, joined by bullets. `flags` are boolean fields shown as small badges of their labels when true, such as a garden or parking.
|
|
22
|
+
* `dots` are related columns of the list (`c.related`), each a line of its dots under the rest of the card, with the
|
|
23
|
+
* colours, order and hover cards they have in the table; a column with no records for the row shows no line.
|
|
22
24
|
* With `card`, a component of the project or a plugin draws each card's content instead, from the row of those
|
|
23
25
|
* fields (`{ row }`); without it, or when it throws, the card is the built-in one. With `photos`, the built-in card's
|
|
24
26
|
* picture gets arrows through the `image` of the records of another resource that point at the card's record, after
|
|
@@ -39,6 +41,7 @@ const displays = {
|
|
|
39
41
|
...fields.meta && { meta: fields.meta.map((ref) => ref.name) },
|
|
40
42
|
...fields.footer && { footer: fields.footer.map((ref) => ref.name) },
|
|
41
43
|
...fields.flags && { flags: fields.flags.map((ref) => ref.name) },
|
|
44
|
+
...fields.dots && { dots: fields.dots.map((column) => column.name) },
|
|
42
45
|
...o.card && { card: customComponent(o.card, "d.cards card") },
|
|
43
46
|
...o.photos && { photos: { ...o.photos } },
|
|
44
47
|
...o.favorite && { favorite: { ...o.favorite } }
|
|
@@ -68,6 +71,27 @@ const displays = {
|
|
|
68
71
|
...fields.area && { area: fields.area.name }
|
|
69
72
|
}),
|
|
70
73
|
/**
|
|
74
|
+
* A column per value of the enum field `by`, in the order the field lists them, each headed by its value label and how
|
|
75
|
+
* many of the list's records have it; with a column for records without a value when the field may be empty. Each
|
|
76
|
+
* column pages through the list API with the list's filter and search, its value added, in the list's order. A card
|
|
77
|
+
* shows `title` (default: the view's title) over `subtitle`, with `badge` and a few small `meta` fields, as the built-in
|
|
78
|
+
* card of `d.cards` does, and opens the record. Dragging a card to another column, or picking it up with Space and
|
|
79
|
+
* moving it with the arrow keys, saves the record with `by` set to that column's value, as one update with its ETag,
|
|
80
|
+
* so the resource's access rules and `validate` decide, and a refusal puts the card back and says why. `by` must be
|
|
81
|
+
* a filterable enum field. Named `board` unless `name` says otherwise. See
|
|
82
|
+
* [Boards](website/src/content/docs/reference/ui-config.mdx#boards).
|
|
83
|
+
*/
|
|
84
|
+
board: (fields, o = {}) => ({
|
|
85
|
+
kind: "board",
|
|
86
|
+
name: o.name ?? "board",
|
|
87
|
+
...naming(o),
|
|
88
|
+
by: fields.by.name,
|
|
89
|
+
...fields.title && { title: fields.title.name },
|
|
90
|
+
...fields.subtitle && { subtitle: fields.subtitle.name },
|
|
91
|
+
...fields.badge && { badge: fields.badge.name },
|
|
92
|
+
...fields.meta && { meta: fields.meta.map((ref) => ref.name) }
|
|
93
|
+
}),
|
|
94
|
+
/**
|
|
71
95
|
* The list drawn by a component of the project, registered in `protobase.ui.tsx` under its name. It gets `props` and
|
|
72
96
|
* reads the rows with `useListDisplay()`; `fields` are the fields it shows, fetched with relation names.
|
|
73
97
|
*/
|
|
@@ -90,6 +114,7 @@ const displayErrors = (list) => {
|
|
|
90
114
|
...names.filter((name) => name === tableDisplay).map(() => `"${tableDisplay}" is the list's own display; give the display another name`),
|
|
91
115
|
...names.filter((name, index) => names.indexOf(name) !== index).map((name) => `display "${name}" is defined twice`),
|
|
92
116
|
...(list.displays ?? []).flatMap((display) => display.kind === "map" && display.cards !== void 0 && !list.displays.some((other) => other.kind === "cards" && other.name === display.cards) ? [`display "${display.name}" names cards "${display.cards}", which is not a cards display of the list`] : []),
|
|
117
|
+
...(list.displays ?? []).flatMap((display) => display.kind === "cards" ? (display.dots ?? []).filter((name) => !list.relatedColumns?.some((column) => column.name === name)).map((name) => `display "${display.name}" shows dots of "${name}", which is not a related column of the list`) : []),
|
|
93
118
|
...(list.displays ?? []).flatMap((display) => display.kind === "map" ? approximateErrors(display).map((error) => `display "${display.name}": ${error}`) : []),
|
|
94
119
|
...list.default === void 0 || list.default === "table" || names.includes(list.default) ? [] : [`default "${list.default}" is not one of the displays (${[tableDisplay, ...names].join(", ")})`]
|
|
95
120
|
];
|
|
@@ -99,7 +124,14 @@ const approximateErrors = (display) => {
|
|
|
99
124
|
if (approximate !== void 0 && exact !== void 0) return ["name approximate or exact, not both"];
|
|
100
125
|
return [...(radius !== void 0 || defaultRadius !== void 0 || area !== void 0) && approximate === void 0 && exact === void 0 ? ["an area around approximate positions needs the approximate or exact field"] : [], ...defaultRadius === void 0 || Number.isFinite(defaultRadius) && defaultRadius > 0 ? [] : [`defaultRadius ${defaultRadius} is not a number of metres above 0`]];
|
|
101
126
|
};
|
|
102
|
-
/**
|
|
127
|
+
/** What makes a board unable to put the records in columns: a `by` field that is no filterable enum. */
|
|
128
|
+
const boardErrors = (display, model) => {
|
|
129
|
+
const field = Object.hasOwn(model.fields, display.by) ? model.fields[display.by] : void 0;
|
|
130
|
+
if (!field) return [];
|
|
131
|
+
if (field.type !== "enum") return [`"${display.by}" on ${model.name} is ${field.type}, not an enum, whose values are the board's columns`];
|
|
132
|
+
return field.filterable ? [] : [`"${display.by}" on ${model.name} is not filterable, and each column of the board filters by its value`];
|
|
133
|
+
};
|
|
134
|
+
/** Every field a display names: what it shows, a map's position and the fields saying how exact it is, a board's columns, and the field references in a custom display's props. */
|
|
103
135
|
const displayFields = (display) => {
|
|
104
136
|
if (display.kind === "map") return [
|
|
105
137
|
display.latitude,
|
|
@@ -110,6 +142,13 @@ const displayFields = (display) => {
|
|
|
110
142
|
display.area
|
|
111
143
|
].filter((name) => name !== void 0);
|
|
112
144
|
if (display.kind === "custom") return [...display.fields, ...fieldRefsIn(display.props)];
|
|
145
|
+
if (display.kind === "board") return [
|
|
146
|
+
display.by,
|
|
147
|
+
display.title,
|
|
148
|
+
display.subtitle,
|
|
149
|
+
display.badge,
|
|
150
|
+
...display.meta ?? []
|
|
151
|
+
].filter((name) => name !== void 0);
|
|
113
152
|
return [
|
|
114
153
|
display.image,
|
|
115
154
|
display.title,
|
|
@@ -122,4 +161,4 @@ const displayFields = (display) => {
|
|
|
122
161
|
].filter((name) => name !== void 0);
|
|
123
162
|
};
|
|
124
163
|
//#endregion
|
|
125
|
-
export { cardPhotoLimit, customComponent, displayErrors, displayFields, displays, tableDisplay };
|
|
164
|
+
export { boardErrors, cardPhotoLimit, customComponent, displayErrors, displayFields, displays, tableDisplay };
|
package/dist/view.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { ActionModel,
|
|
1
|
+
import { ActionModel, 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 { ChartInput } from "./view-chart.js";
|
|
4
5
|
import { ListColumn, columns } from "./view-columns.js";
|
|
5
6
|
import { displays } from "./view-displays.js";
|
|
6
7
|
import { FieldView } from "./view-field.js";
|
|
@@ -66,14 +67,17 @@ export declare class ViewBuilder<R extends {
|
|
|
66
67
|
/** Hints per field, such as `{ total: r.total.prefix('€').decimals(2) }`; calls add up. */
|
|
67
68
|
fields(build: (r: FieldViews<R>) => Record<string, FieldView>): ViewBuilder<R>;
|
|
68
69
|
/**
|
|
69
|
-
* The list: its table's `columns`, fields or columns of related records (`c.related`), `sort` and `search`,
|
|
70
|
-
* `displays` to switch to (`d.cards`, `d.custom`), one of which
|
|
71
|
-
* display or column names collide
|
|
70
|
+
* The list: its table's `columns`, fields or columns of related records (`c.related`), `sort` and `search`, the
|
|
71
|
+
* columns that are `editable` in their cell, and other `displays` to switch to (`d.cards`, `d.map`, `d.board`, `d.custom`), one of which
|
|
72
|
+
* may be the `default` the list opens in. Throws when display or column names collide, `default` names none or an
|
|
73
|
+
* editable field is not among the columns. See
|
|
74
|
+
* [Editing in the list](website/src/content/docs/reference/ui-config.mdx#editing-in-the-list).
|
|
72
75
|
*/
|
|
73
76
|
list(build: (r: FieldRefs<FieldsOf<R>>, d: typeof displays, c: typeof columns) => {
|
|
74
77
|
columns: Array<Ref | ListColumn>;
|
|
75
78
|
sort?: Array<[Ref, 'asc' | 'desc']>;
|
|
76
79
|
search?: Ref[];
|
|
80
|
+
editable?: Ref[];
|
|
77
81
|
displays?: ListDisplayModel[];
|
|
78
82
|
default?: string;
|
|
79
83
|
}): ViewBuilder<R>;
|
|
@@ -87,12 +91,12 @@ export declare class ViewBuilder<R extends {
|
|
|
87
91
|
* Without it the page shows one section with every field.
|
|
88
92
|
*/
|
|
89
93
|
layout(build: (r: FieldRefs<FieldsOf<R>>) => LayoutItemModel[]): ViewBuilder<R>;
|
|
90
|
-
/**
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
94
|
+
/**
|
|
95
|
+
* A chart above the list, following its filters: records per `granularity` over a date or timestamp `field`, or
|
|
96
|
+
* records per value of `groupBy`, such as a status or a relation, as bars or a donut of the `limit` largest (default
|
|
97
|
+
* 10), counted or with `sum` or `average` that of a numeric field.
|
|
98
|
+
*/
|
|
99
|
+
chart(build: (r: FieldRefs<FieldsOf<R>>) => ChartInput): ViewBuilder<R>;
|
|
96
100
|
/**
|
|
97
101
|
* How the record page opens to a user who may change the record: `edit`, the default, as its form; `read` as a read
|
|
98
102
|
* view, its sections as tables of labels and values, with an Edit button to the form; `locked` as the form, locked
|
package/dist/view.js
CHANGED
|
@@ -2,8 +2,9 @@ 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 { chartModel } from "./view-chart.js";
|
|
5
6
|
import { columns, isListColumn } from "./view-columns.js";
|
|
6
|
-
import { displayErrors, displayFields, displays } from "./view-displays.js";
|
|
7
|
+
import { boardErrors, displayErrors, displayFields, displays } from "./view-displays.js";
|
|
7
8
|
import { FieldView } from "./view-field.js";
|
|
8
9
|
import { widgets } from "./view-widgets.js";
|
|
9
10
|
//#region src/view.ts
|
|
@@ -73,21 +74,26 @@ var ViewBuilder = class ViewBuilder {
|
|
|
73
74
|
} });
|
|
74
75
|
}
|
|
75
76
|
/**
|
|
76
|
-
* The list: its table's `columns`, fields or columns of related records (`c.related`), `sort` and `search`,
|
|
77
|
-
* `displays` to switch to (`d.cards`, `d.custom`), one of which
|
|
78
|
-
* display or column names collide
|
|
77
|
+
* The list: its table's `columns`, fields or columns of related records (`c.related`), `sort` and `search`, the
|
|
78
|
+
* columns that are `editable` in their cell, and other `displays` to switch to (`d.cards`, `d.map`, `d.board`, `d.custom`), one of which
|
|
79
|
+
* may be the `default` the list opens in. Throws when display or column names collide, `default` names none or an
|
|
80
|
+
* editable field is not among the columns. See
|
|
81
|
+
* [Editing in the list](website/src/content/docs/reference/ui-config.mdx#editing-in-the-list).
|
|
79
82
|
*/
|
|
80
83
|
list(build) {
|
|
81
|
-
const { columns: columns$1, sort, search, displays: shown = [], default: opens } = build(this.refs(), displays, columns);
|
|
84
|
+
const { columns: columns$1, sort, search, editable, displays: shown = [], default: opens } = build(this.refs(), displays, columns);
|
|
82
85
|
const related = columns$1.filter(isListColumn).map(({ kind: _, ...column }) => column);
|
|
83
86
|
const names = columns$1.map((column) => column.name);
|
|
84
87
|
const repeated = names.find((name, index) => names.indexOf(name) !== index);
|
|
85
88
|
if (repeated !== void 0) throw new Error(`View "${this.state.resource}": the list has two columns named "${repeated}"`);
|
|
89
|
+
const stray = editable?.find((ref) => !names.includes(ref.name));
|
|
90
|
+
if (stray) throw new Error(`View "${this.state.resource}": editable field "${stray.name}" is not among the list's columns`);
|
|
86
91
|
const list = {
|
|
87
92
|
columns: names,
|
|
88
93
|
...related.length > 0 && { relatedColumns: related },
|
|
89
94
|
...sort && { sort: sort.map(([ref, dir]) => [ref.name, dir]) },
|
|
90
95
|
...search && { search: search.map((ref) => ref.name) },
|
|
96
|
+
...editable && editable.length > 0 && { editable: editable.map((ref) => ref.name) },
|
|
91
97
|
...shown.length > 0 && { displays: shown },
|
|
92
98
|
...opens !== void 0 && { default: opens }
|
|
93
99
|
};
|
|
@@ -109,14 +115,13 @@ var ViewBuilder = class ViewBuilder {
|
|
|
109
115
|
layout(build) {
|
|
110
116
|
return this.next({ layout: build(this.refs()) });
|
|
111
117
|
}
|
|
112
|
-
/**
|
|
118
|
+
/**
|
|
119
|
+
* A chart above the list, following its filters: records per `granularity` over a date or timestamp `field`, or
|
|
120
|
+
* records per value of `groupBy`, such as a status or a relation, as bars or a donut of the `limit` largest (default
|
|
121
|
+
* 10), counted or with `sum` or `average` that of a numeric field.
|
|
122
|
+
*/
|
|
113
123
|
chart(build) {
|
|
114
|
-
|
|
115
|
-
return this.next({ chart: {
|
|
116
|
-
field: field.name,
|
|
117
|
-
range,
|
|
118
|
-
granularity
|
|
119
|
-
} });
|
|
124
|
+
return this.next({ chart: chartModel(build(this.refs())) });
|
|
120
125
|
}
|
|
121
126
|
/**
|
|
122
127
|
* How the record page opens to a user who may change the record: `edit`, the default, as its form; `read` as a read
|
|
@@ -237,6 +242,8 @@ var ViewBuilder = class ViewBuilder {
|
|
|
237
242
|
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`);
|
|
238
243
|
const flag = display.kind === "cards" ? display.flags?.find((name) => resource.fields[name].type !== "boolean") : void 0;
|
|
239
244
|
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`);
|
|
245
|
+
const board = display.kind === "board" ? boardErrors(display, resource) : [];
|
|
246
|
+
if (board.length > 0) throw new Error(`View "${this.state.resource}": display "${display.name}": ${board.join("; ")}`);
|
|
240
247
|
}
|
|
241
248
|
const clash = resource ? list?.relatedColumns?.find((column) => Object.hasOwn(resource.fields, column.name)) : void 0;
|
|
242
249
|
if (clash) throw new Error(`View "${this.state.resource}": related column "${clash.name}" has the name of a field of the resource`);
|