@happyvertical/smrt-svelte 0.55.23 → 0.55.24
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/AGENTS.md +2 -2
- package/README.md +2 -2
- package/agents/overview-surfaces.md +108 -4
- package/agents/screens.md +171 -5
- package/dist/components/overview/index.d.ts +4 -1
- package/dist/components/overview/index.d.ts.map +1 -1
- package/dist/components/overview/index.js +3 -0
- package/dist/components/overview/record.d.ts +51 -0
- package/dist/components/overview/record.d.ts.map +1 -0
- package/dist/components/overview/record.js +146 -0
- package/dist/components/overview/server.d.ts +4 -0
- package/dist/components/overview/server.d.ts.map +1 -1
- package/dist/components/overview/server.js +4 -0
- package/dist/components/overview/types.d.ts +12 -0
- package/dist/components/overview/types.d.ts.map +1 -1
- package/dist/components/overview/widgets/LineItemsWidget.svelte +144 -0
- package/dist/components/overview/widgets/LineItemsWidget.svelte.d.ts +6 -0
- package/dist/components/overview/widgets/LineItemsWidget.svelte.d.ts.map +1 -0
- package/dist/components/overview/widgets/RecordListWidget.svelte +67 -3
- package/dist/components/overview/widgets/RecordListWidget.svelte.d.ts.map +1 -1
- package/dist/components/overview/widgets/RecordTable.svelte +167 -0
- package/dist/components/overview/widgets/RecordTable.svelte.d.ts +21 -0
- package/dist/components/overview/widgets/RecordTable.svelte.d.ts.map +1 -0
- package/dist/components/overview/widgets/RelatedWidget.svelte +117 -0
- package/dist/components/overview/widgets/RelatedWidget.svelte.d.ts +6 -0
- package/dist/components/overview/widgets/RelatedWidget.svelte.d.ts.map +1 -0
- package/dist/components/overview/widgets/core.d.ts +13 -2
- package/dist/components/overview/widgets/core.d.ts.map +1 -1
- package/dist/components/overview/widgets/core.js +43 -2
- package/dist/components/overview/widgets/data.d.ts +97 -8
- package/dist/components/overview/widgets/data.d.ts.map +1 -1
- package/dist/components/overview/widgets/line-totals.d.ts +48 -0
- package/dist/components/overview/widgets/line-totals.d.ts.map +1 -0
- package/dist/components/overview/widgets/line-totals.js +91 -0
- package/dist/components/overview/widgets/record-loaders.d.ts +63 -0
- package/dist/components/overview/widgets/record-loaders.d.ts.map +1 -0
- package/dist/components/overview/widgets/record-loaders.js +150 -0
- package/dist/components/overview/widgets/record-table.d.ts +45 -0
- package/dist/components/overview/widgets/record-table.d.ts.map +1 -0
- package/dist/components/overview/widgets/record-table.js +201 -0
- package/dist/components/overview/widgets/record-widgets.d.ts +27 -0
- package/dist/components/overview/widgets/record-widgets.d.ts.map +1 -0
- package/dist/components/overview/widgets/record-widgets.js +100 -0
- package/dist/components/screens/DetailScreen.svelte +105 -6
- package/dist/components/screens/DetailScreen.svelte.d.ts.map +1 -1
- package/dist/components/screens/EditForm.svelte +39 -3
- package/dist/components/screens/EditForm.svelte.d.ts.map +1 -1
- package/dist/components/screens/RecipeScreens.svelte +18 -18
- package/dist/components/screens/RecipeScreens.svelte.d.ts.map +1 -1
- package/dist/components/screens/RecordHeader.svelte +238 -0
- package/dist/components/screens/RecordHeader.svelte.d.ts +26 -0
- package/dist/components/screens/RecordHeader.svelte.d.ts.map +1 -0
- package/dist/components/screens/RecordScreen.svelte +511 -0
- package/dist/components/screens/RecordScreen.svelte.d.ts +5 -0
- package/dist/components/screens/RecordScreen.svelte.d.ts.map +1 -0
- package/dist/components/screens/columns.d.ts +29 -0
- package/dist/components/screens/columns.d.ts.map +1 -0
- package/dist/components/screens/columns.js +115 -0
- package/dist/components/screens/fields.d.ts +12 -2
- package/dist/components/screens/fields.d.ts.map +1 -1
- package/dist/components/screens/fields.js +37 -5
- package/dist/components/screens/index.d.ts +7 -2
- package/dist/components/screens/index.d.ts.map +1 -1
- package/dist/components/screens/index.js +6 -1
- package/dist/components/screens/record-header.d.ts +51 -0
- package/dist/components/screens/record-header.d.ts.map +1 -0
- package/dist/components/screens/record-header.js +118 -0
- package/dist/components/screens/relations.d.ts +49 -0
- package/dist/components/screens/relations.d.ts.map +1 -0
- package/dist/components/screens/relations.js +98 -0
- package/dist/components/screens/roles.d.ts +56 -0
- package/dist/components/screens/roles.d.ts.map +1 -0
- package/dist/components/screens/roles.js +174 -0
- package/dist/components/screens/types.d.ts +182 -1
- package/dist/components/screens/types.d.ts.map +1 -1
- package/dist/components/screens/values.d.ts +23 -1
- package/dist/components/screens/values.d.ts.map +1 -1
- package/dist/components/screens/values.js +80 -4
- package/dist/i18n/strings.overview.d.ts +25 -0
- package/dist/i18n/strings.overview.d.ts.map +1 -1
- package/dist/i18n/strings.overview.js +25 -0
- package/dist/i18n/strings.screens.d.ts +8 -0
- package/dist/i18n/strings.screens.d.ts.map +1 -1
- package/dist/i18n/strings.screens.js +8 -0
- package/package.json +16 -16
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/** Every role, in the order a role list is reported. */
|
|
2
|
+
export const FIELD_ROLES = [
|
|
3
|
+
'title',
|
|
4
|
+
'identifier',
|
|
5
|
+
'status',
|
|
6
|
+
'money',
|
|
7
|
+
'quantity',
|
|
8
|
+
'date',
|
|
9
|
+
'party',
|
|
10
|
+
'subtotal',
|
|
11
|
+
'tax',
|
|
12
|
+
'discount',
|
|
13
|
+
'total',
|
|
14
|
+
];
|
|
15
|
+
const NUMERIC = new Set(['integer', 'decimal']);
|
|
16
|
+
const REFERENCE = new Set(['foreignKey', 'crossPackageRef']);
|
|
17
|
+
/** `*Amount`, `*Total`, `price`, `cost` (and the totals roles' own names). */
|
|
18
|
+
const MONEY_NAME = /(?:^|[a-z])(?:amount|total|price|cost)$|^(?:subtotal|tax|discount)$/i;
|
|
19
|
+
const SUBTOTAL_NAME = /^subtotal(?:Amount)?$|[a-z]Subtotal(?:Amount)?$/;
|
|
20
|
+
const TAX_NAME = /^tax(?:Amount|Total)?$|[a-z]Tax(?:Amount|Total)?$/;
|
|
21
|
+
const DISCOUNT_NAME = /^discount(?:Amount|Total)?$|[a-z]Discount(?:Amount|Total)?$/;
|
|
22
|
+
const TOTAL_NAME = /^(?:total|grandTotal)(?:Amount)?$|^amountTotal$/;
|
|
23
|
+
const RATE_NAME = /(?:rate|percent|percentage|pct|ratio)$/i;
|
|
24
|
+
const STATUS_NAME = /^(?:status|state)$|[a-z](?:Status|State)$/;
|
|
25
|
+
const IDENTIFIER_NAME = /^(?:number|reference|code|ref)$|[a-z](?:Number|Reference|Code)$/;
|
|
26
|
+
/** `phoneNumber`, `postalCode`, `currencyCode`, ... are not the record's identifier. */
|
|
27
|
+
const NOT_IDENTIFIER = /^(?:phone|mobile|fax|postal|zip|country|currency|language|locale|tax|vat|account|routing|card|area|promo|coupon|discount|sort|tracking|billing|shipping|iban|swift|bank|unit|line|street|house|building)/i;
|
|
28
|
+
const DATE_NAME = /^date$|[a-z](?:At|Date|On|Until)$/;
|
|
29
|
+
const QUANTITY_NAME = /^(?:quantity|qty)|(?:Quantity|Qty)$/;
|
|
30
|
+
const TITLE_NAMES = ['name', 'title', 'label', 'displayName'];
|
|
31
|
+
/** A party: who a record is with. Matched against the FK name and its target. */
|
|
32
|
+
const PARTY_NAME = /customer|vendor|supplier|client|party|profile|contact|person|people|member|company|organi[sz]ation|payer|payee|buyer|seller|licensee|lessee/i;
|
|
33
|
+
function explicitRoles(definition) {
|
|
34
|
+
const role = definition.role;
|
|
35
|
+
if (role === undefined)
|
|
36
|
+
return null;
|
|
37
|
+
const list = (Array.isArray(role) ? role : [role]);
|
|
38
|
+
return list.filter((r) => FIELD_ROLES.includes(r));
|
|
39
|
+
}
|
|
40
|
+
function formatOf(definition) {
|
|
41
|
+
return definition.format ?? definition.ui?.format;
|
|
42
|
+
}
|
|
43
|
+
/** True when a field declares money through metadata or a manifest hint. */
|
|
44
|
+
function hintedMoney(definition) {
|
|
45
|
+
if (formatOf(definition) === 'money')
|
|
46
|
+
return true;
|
|
47
|
+
return definition.type === 'integer' && definition.ui?.widget === 'currency';
|
|
48
|
+
}
|
|
49
|
+
function hasEnum(definition) {
|
|
50
|
+
return Array.isArray(definition.enum) && definition.enum.length > 0;
|
|
51
|
+
}
|
|
52
|
+
function relationshipFor(relationships, field) {
|
|
53
|
+
return relationships?.find((edge) => edge.field === field);
|
|
54
|
+
}
|
|
55
|
+
function heuristicRoles(name, definition, relationship) {
|
|
56
|
+
const roles = [];
|
|
57
|
+
const { type } = definition;
|
|
58
|
+
const numeric = NUMERIC.has(type);
|
|
59
|
+
const rate = RATE_NAME.test(name);
|
|
60
|
+
if (type === 'text') {
|
|
61
|
+
if (hasEnum(definition) && STATUS_NAME.test(name))
|
|
62
|
+
roles.push('status');
|
|
63
|
+
if (IDENTIFIER_NAME.test(name) &&
|
|
64
|
+
!NOT_IDENTIFIER.test(name) &&
|
|
65
|
+
definition.ui?.widget !== 'phone') {
|
|
66
|
+
roles.push('identifier');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (type === 'integer' && !rate && MONEY_NAME.test(name))
|
|
70
|
+
roles.push('money');
|
|
71
|
+
if (numeric && QUANTITY_NAME.test(name))
|
|
72
|
+
roles.push('quantity');
|
|
73
|
+
if (type === 'datetime' && DATE_NAME.test(name))
|
|
74
|
+
roles.push('date');
|
|
75
|
+
if (REFERENCE.has(type)) {
|
|
76
|
+
const target = relationship?.relatedCollection ?? '';
|
|
77
|
+
const base = name.replace(/Id$/, '');
|
|
78
|
+
if (base !== 'tenant' && (PARTY_NAME.test(base) || PARTY_NAME.test(target)))
|
|
79
|
+
roles.push('party');
|
|
80
|
+
}
|
|
81
|
+
if (numeric && !rate) {
|
|
82
|
+
if (SUBTOTAL_NAME.test(name))
|
|
83
|
+
roles.push('subtotal');
|
|
84
|
+
else if (TAX_NAME.test(name))
|
|
85
|
+
roles.push('tax');
|
|
86
|
+
else if (DISCOUNT_NAME.test(name))
|
|
87
|
+
roles.push('discount');
|
|
88
|
+
else if (TOTAL_NAME.test(name))
|
|
89
|
+
roles.push('total');
|
|
90
|
+
}
|
|
91
|
+
return roles;
|
|
92
|
+
}
|
|
93
|
+
function ordered(roles) {
|
|
94
|
+
const set = new Set(roles);
|
|
95
|
+
return FIELD_ROLES.filter((r) => set.has(r));
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Roles of every declared field of `definition` (a key per field, possibly
|
|
99
|
+
* an empty list). Without an explicit `display` field, the first text field
|
|
100
|
+
* named `name` / `title` / `label` / `displayName` takes the `title` role.
|
|
101
|
+
*/
|
|
102
|
+
export function deriveFieldRoles(definition) {
|
|
103
|
+
const out = {};
|
|
104
|
+
const entries = Object.entries(definition.fields).filter(([name]) => !name.startsWith('_') && !name.startsWith('$'));
|
|
105
|
+
const hasDisplay = entries.some(([, d]) => d.display === true);
|
|
106
|
+
for (const [name, fieldDefinition] of entries) {
|
|
107
|
+
const roles = new Set();
|
|
108
|
+
const explicit = explicitRoles(fieldDefinition);
|
|
109
|
+
if (explicit)
|
|
110
|
+
for (const r of explicit)
|
|
111
|
+
roles.add(r);
|
|
112
|
+
else {
|
|
113
|
+
for (const r of heuristicRoles(name, fieldDefinition, relationshipFor(definition.relationships, name)))
|
|
114
|
+
roles.add(r);
|
|
115
|
+
}
|
|
116
|
+
if (hintedMoney(fieldDefinition))
|
|
117
|
+
roles.add('money');
|
|
118
|
+
const format = formatOf(fieldDefinition);
|
|
119
|
+
if ((format === 'date' || format === 'datetime') &&
|
|
120
|
+
fieldDefinition.type === 'datetime') {
|
|
121
|
+
roles.add('date');
|
|
122
|
+
}
|
|
123
|
+
if (fieldDefinition.display === true)
|
|
124
|
+
roles.add('title');
|
|
125
|
+
out[name] = ordered(roles);
|
|
126
|
+
}
|
|
127
|
+
if (!hasDisplay && !Object.values(out).some((r) => r.includes('title'))) {
|
|
128
|
+
for (const candidate of TITLE_NAMES) {
|
|
129
|
+
const fieldDefinition = definition.fields[candidate];
|
|
130
|
+
if (fieldDefinition?.type === 'text' &&
|
|
131
|
+
explicitRoles(fieldDefinition) === null) {
|
|
132
|
+
out[candidate] = ordered([...(out[candidate] ?? []), 'title']);
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return out;
|
|
138
|
+
}
|
|
139
|
+
/** Fields holding `role`, in declaration order. */
|
|
140
|
+
export function fieldsWithRole(roles, role) {
|
|
141
|
+
return Object.keys(roles).filter((name) => roles[name]?.includes(role));
|
|
142
|
+
}
|
|
143
|
+
/** The first field holding `role`, or `undefined`. */
|
|
144
|
+
export function fieldWithRole(roles, role) {
|
|
145
|
+
return Object.keys(roles).find((name) => roles[name]?.includes(role));
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* The field naming a record's currency: a text field hinted
|
|
149
|
+
* `ui.widget: 'currency'` (the manifest's currency-code picker), else a text
|
|
150
|
+
* field named `currency` / `currencyCode`.
|
|
151
|
+
*/
|
|
152
|
+
export function currencyCodeField(definition) {
|
|
153
|
+
const entries = Object.entries(definition.fields);
|
|
154
|
+
return (entries.find(([, d]) => d.type === 'text' && d.ui?.widget === 'currency')?.[0] ??
|
|
155
|
+
entries.find(([name, d]) => d.type === 'text' && /^currency(?:Code)?$/.test(name))?.[0]);
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Whether records of this definition read like documents (an order, an
|
|
159
|
+
* invoice): an identifier plus a party or a total. Their header leads with
|
|
160
|
+
* the identifier ("Order GRA-3159") rather than a name.
|
|
161
|
+
*/
|
|
162
|
+
export function isDocumentLike(roles) {
|
|
163
|
+
return (fieldWithRole(roles, 'identifier') !== undefined &&
|
|
164
|
+
(fieldWithRole(roles, 'party') !== undefined ||
|
|
165
|
+
fieldWithRole(roles, 'total') !== undefined));
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Whether a child definition has line-item roles: a quantity and a money
|
|
169
|
+
* field (`deriveScreenRelations` classifies such a child relation `items`).
|
|
170
|
+
*/
|
|
171
|
+
export function hasLineItemRoles(roles) {
|
|
172
|
+
return (fieldWithRole(roles, 'quantity') !== undefined &&
|
|
173
|
+
fieldWithRole(roles, 'money') !== undefined);
|
|
174
|
+
}
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { WidgetRegistry } from '../overview/registry.js';
|
|
3
|
+
import type { OverviewDefinition, OverviewOverride, WidgetLoadContext } from '../overview/types.js';
|
|
4
|
+
import type { CoreWidgetLoaders } from '../overview/widgets/core.js';
|
|
5
|
+
import type { RecordStatusTone } from '../overview/widgets/data.js';
|
|
6
|
+
import type { ScreenRelation } from './relations.js';
|
|
7
|
+
import type { FieldRole } from './roles.js';
|
|
2
8
|
/**
|
|
3
9
|
* Structural types for the manifest-derived record screens (#3718).
|
|
4
10
|
*
|
|
@@ -10,13 +16,25 @@ import type { Snippet } from 'svelte';
|
|
|
10
16
|
*/
|
|
11
17
|
/** Persisted public wire types emitted by generated web collections. */
|
|
12
18
|
export type ScreenFieldType = 'text' | 'integer' | 'decimal' | 'boolean' | 'datetime' | 'json' | 'foreignKey' | 'crossPackageRef';
|
|
19
|
+
/**
|
|
20
|
+
* How a value is presented: `money` (integer minor units), `date` (a
|
|
21
|
+
* business date, shown without a time), `datetime`, `percent` (a rate, `0.08`
|
|
22
|
+
* shows as `8%`).
|
|
23
|
+
*/
|
|
24
|
+
export type ScreenFieldFormat = 'money' | 'date' | 'datetime' | 'percent';
|
|
13
25
|
/** Static `@field({ ui })` presentation hints from the manifest. */
|
|
14
26
|
export interface ScreenFieldUIHints {
|
|
15
27
|
basic?: boolean;
|
|
16
28
|
group?: string;
|
|
17
29
|
order?: number;
|
|
18
30
|
locked?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* `currency` on a TEXT field is the currency-code picker (the manifest
|
|
33
|
+
* meaning); on an INTEGER field it is read as the legacy money hint.
|
|
34
|
+
*/
|
|
19
35
|
widget?: 'textarea' | 'currency' | 'email' | 'url' | 'phone';
|
|
36
|
+
/** Manifest presentation format (`@field({ ui: { format } })`). */
|
|
37
|
+
format?: ScreenFieldFormat;
|
|
20
38
|
}
|
|
21
39
|
/** Browser-safe subset of one generated web field definition. */
|
|
22
40
|
export interface ScreenFieldDefinition {
|
|
@@ -27,9 +45,40 @@ export interface ScreenFieldDefinition {
|
|
|
27
45
|
/** Developer-authored `@field({ description })`; the code-seed help text. */
|
|
28
46
|
description?: string;
|
|
29
47
|
ui?: ScreenFieldUIHints;
|
|
48
|
+
/**
|
|
49
|
+
* What the field means on a record page (`deriveFieldRoles`). Explicit
|
|
50
|
+
* roles are authoritative: name heuristics never add to them.
|
|
51
|
+
*/
|
|
52
|
+
role?: FieldRole | readonly FieldRole[];
|
|
53
|
+
/** Allowed values; a text field with an enum edits as a select. */
|
|
54
|
+
enum?: readonly string[];
|
|
55
|
+
/** Presentation format; wins over `ui.format`. */
|
|
56
|
+
format?: ScreenFieldFormat;
|
|
57
|
+
/** This field is the record's display label (the `title` role). */
|
|
58
|
+
display?: boolean;
|
|
59
|
+
}
|
|
60
|
+
export type ScreenRelationshipKind = 'foreignKey' | 'crossPackageRef' | 'oneToMany' | 'manyToMany';
|
|
61
|
+
/**
|
|
62
|
+
* One manifest-derived relationship edge, structurally the generated
|
|
63
|
+
* `SmrtWebRelationship`: `field` is the FK field on this model and
|
|
64
|
+
* `relatedCollection` the sibling collection's name (the key in
|
|
65
|
+
* `collectionDefinitions`), not a qualified model id.
|
|
66
|
+
*/
|
|
67
|
+
export interface ScreenRelationshipDefinition {
|
|
68
|
+
field: string;
|
|
69
|
+
kind: ScreenRelationshipKind;
|
|
70
|
+
relatedCollection: string;
|
|
71
|
+
/**
|
|
72
|
+
* `items`: the declaring (child) model is the line items of the target
|
|
73
|
+
* (`@foreignKey(Order, { role: 'items' })`); `deriveScreenRelations`
|
|
74
|
+
* classifies the relation `items`.
|
|
75
|
+
*/
|
|
76
|
+
role?: 'items';
|
|
30
77
|
}
|
|
31
78
|
/** Structural subset of a generated `SmrtWebCollectionDefinition`. */
|
|
32
79
|
export interface ScreenCollectionDefinition {
|
|
80
|
+
/** Collection name (the key in `collectionDefinitions`); used to resolve relationships. */
|
|
81
|
+
name?: string;
|
|
33
82
|
/** Canonical qualified model identity (`@package/name:ClassName`). */
|
|
34
83
|
objectRef: string;
|
|
35
84
|
/** Simple class name, used to derive default titles. */
|
|
@@ -37,6 +86,8 @@ export interface ScreenCollectionDefinition {
|
|
|
37
86
|
/** Primary-key field name. Defaults to `id`. */
|
|
38
87
|
idField?: string;
|
|
39
88
|
fields: Readonly<Record<string, ScreenFieldDefinition>>;
|
|
89
|
+
/** Manifest-derived relationship edges; the source of `deriveScreenRelations`. */
|
|
90
|
+
relationships?: readonly ScreenRelationshipDefinition[];
|
|
40
91
|
}
|
|
41
92
|
export type ScreenFieldVisibility = 'basic' | 'advanced' | 'hidden';
|
|
42
93
|
/**
|
|
@@ -61,7 +112,7 @@ export interface ScreenPolicy {
|
|
|
61
112
|
}
|
|
62
113
|
export type ScreenMode = 'list' | 'view' | 'edit';
|
|
63
114
|
/** How a field is rendered and edited. */
|
|
64
|
-
export type ScreenInputKind = 'text' | 'textarea' | 'email' | 'url' | 'tel' | 'integer' | 'decimal' | 'money' | 'boolean' | 'datetime' | 'json' | 'reference';
|
|
115
|
+
export type ScreenInputKind = 'text' | 'textarea' | 'email' | 'url' | 'tel' | 'integer' | 'decimal' | 'money' | 'boolean' | 'datetime' | 'json' | 'reference' | 'select';
|
|
65
116
|
/** A field selected and decorated for one screen. */
|
|
66
117
|
export interface ScreenField {
|
|
67
118
|
name: string;
|
|
@@ -76,6 +127,10 @@ export interface ScreenField {
|
|
|
76
127
|
group: string | null;
|
|
77
128
|
order: number | null;
|
|
78
129
|
definition: ScreenFieldDefinition;
|
|
130
|
+
/** The field's roles (`deriveFieldRoles`); set by `deriveScreenFields`. */
|
|
131
|
+
roles?: readonly FieldRole[];
|
|
132
|
+
/** Choices of a `select` field (the definition's `enum`). */
|
|
133
|
+
options?: readonly string[];
|
|
79
134
|
}
|
|
80
135
|
/** Why a draft value could not become a payload value. */
|
|
81
136
|
export type ScreenFieldErrorCode = 'required' | 'invalid_integer' | 'invalid_decimal' | 'invalid_money' | 'invalid_datetime' | 'invalid_json';
|
|
@@ -156,6 +211,27 @@ export interface DetailScreenProps {
|
|
|
156
211
|
extraActions?: Snippet;
|
|
157
212
|
/** Extra content under the fields. */
|
|
158
213
|
children?: Snippet;
|
|
214
|
+
/**
|
|
215
|
+
* Relations of this model (`deriveScreenRelations`). The `reference` ones
|
|
216
|
+
* map a FK field to its target model, which `labels` is keyed by.
|
|
217
|
+
*/
|
|
218
|
+
relations?: readonly ScreenRelation[];
|
|
219
|
+
/** Link for a reference field's value; `undefined` renders no link. */
|
|
220
|
+
hrefFor?: (field: ScreenField, id: string) => string | undefined;
|
|
221
|
+
/**
|
|
222
|
+
* Display label for a referenced record, keyed by target model id
|
|
223
|
+
* (`relation.model`); `undefined` falls back to the raw id.
|
|
224
|
+
*/
|
|
225
|
+
labels?: Readonly<Record<string, (id: string) => string | undefined>>;
|
|
226
|
+
/** Field names to leave out (the record header shows them). */
|
|
227
|
+
exclude?: readonly string[];
|
|
228
|
+
/**
|
|
229
|
+
* Move fields with no value behind a "Show empty fields" disclosure
|
|
230
|
+
* instead of listing "Not set" cells.
|
|
231
|
+
*/
|
|
232
|
+
hideEmpty?: boolean;
|
|
233
|
+
/** `card`: the fields sit in a bordered card (the record page's details card). */
|
|
234
|
+
variant?: 'plain' | 'card';
|
|
159
235
|
}
|
|
160
236
|
/** What a submit handler may return to report server-side field problems. */
|
|
161
237
|
export interface EditFormSubmitResult {
|
|
@@ -180,12 +256,110 @@ export interface EditFormProps {
|
|
|
180
256
|
fieldErrors?: Readonly<Record<string, string>>;
|
|
181
257
|
/** A form-level failure message from the host. */
|
|
182
258
|
error?: string | null;
|
|
259
|
+
/**
|
|
260
|
+
* Fields computed elsewhere (a subtotal / tax / total worked out from the
|
|
261
|
+
* line items): shown read-only with a hint and never part of the payload.
|
|
262
|
+
*/
|
|
263
|
+
derivedFields?: readonly string[];
|
|
183
264
|
/** Receives wire-ready values for the displayed fields. */
|
|
184
265
|
onsubmit: (values: Record<string, unknown>, context: {
|
|
185
266
|
isNew: boolean;
|
|
186
267
|
}) => void | EditFormSubmitResult | Promise<void | EditFormSubmitResult>;
|
|
187
268
|
oncancel?: () => void;
|
|
188
269
|
}
|
|
270
|
+
export type RecordScreenMode = 'view' | 'edit';
|
|
271
|
+
/**
|
|
272
|
+
* Props of `RecordScreen`: the record view (#3718 record view). The detail
|
|
273
|
+
* header and fields sit at the top, a customizable overview of related
|
|
274
|
+
* records below; `mode: 'edit'` opens the edit form beside the header on
|
|
275
|
+
* desktop and in a full-screen sheet on phones.
|
|
276
|
+
*/
|
|
277
|
+
export interface RecordScreenProps {
|
|
278
|
+
definition: ScreenCollectionDefinition;
|
|
279
|
+
/** Resolved field policy for the same object; omit for the code seed. */
|
|
280
|
+
policy?: ScreenPolicy | null;
|
|
281
|
+
record: ScreenRecord;
|
|
282
|
+
/** Relations of this model (`deriveScreenRelations`). */
|
|
283
|
+
relations?: readonly ScreenRelation[];
|
|
284
|
+
mode: RecordScreenMode;
|
|
285
|
+
/**
|
|
286
|
+
* Loaders of the core widgets; the shell registers them on a registry of
|
|
287
|
+
* its own and runs them in the browser with the record context. A widget
|
|
288
|
+
* without a loader is not offered (and a default using it is dropped).
|
|
289
|
+
*/
|
|
290
|
+
loaders?: CoreWidgetLoaders;
|
|
291
|
+
/** An explicit registry (recipe widgets included) instead of one built from `loaders`. */
|
|
292
|
+
registry?: WidgetRegistry;
|
|
293
|
+
/**
|
|
294
|
+
* Host capabilities for the widget loads (a `fetch`, a client, the
|
|
295
|
+
* locale). `overviewId` and `record` are set by the screen.
|
|
296
|
+
*/
|
|
297
|
+
widgetContext?: Omit<WidgetLoadContext, 'overviewId' | 'record'>;
|
|
298
|
+
/** The overview to render; defaults to `recordOverview(definition, relations)`. */
|
|
299
|
+
overview?: OverviewDefinition;
|
|
300
|
+
/** The stored layout override for this model kind (host-owned when given). */
|
|
301
|
+
override?: OverviewOverride | null;
|
|
302
|
+
/** Receives the override after each layout edit (`null` = defaults). */
|
|
303
|
+
onoverviewchange?: (override: OverviewOverride | null) => void | Promise<void>;
|
|
304
|
+
/** Layout edit mode; omit to follow the shell's layout editor. */
|
|
305
|
+
layoutEditing?: boolean;
|
|
306
|
+
/** Whether the viewer may customize the layout (default true). */
|
|
307
|
+
canCustomize?: boolean;
|
|
308
|
+
/** Link for a reference field's value. */
|
|
309
|
+
hrefFor?: (field: ScreenField, id: string) => string | undefined;
|
|
310
|
+
/** Display labels for referenced records, keyed by target model id. */
|
|
311
|
+
labels?: Readonly<Record<string, (id: string) => string | undefined>>;
|
|
312
|
+
/** "View all" link of a related-records panel; `undefined` renders none. */
|
|
313
|
+
viewAllHref?: (relation: ScreenRelation) => string | undefined;
|
|
314
|
+
/** Page title. Defaults to the first basic text field, else the class name. */
|
|
315
|
+
title?: string;
|
|
316
|
+
/** Explicit field names, in order (overrides the derived set). */
|
|
317
|
+
fields?: readonly string[];
|
|
318
|
+
/** ISO 4217 code used by money fields. */
|
|
319
|
+
currency?: string;
|
|
320
|
+
/** BCP-47 locale for number and date formatting. */
|
|
321
|
+
locale?: string;
|
|
322
|
+
/** Label for the back action; defaults to the pluralized class name. */
|
|
323
|
+
backLabel?: string;
|
|
324
|
+
/** Mutation in flight; locks the delete confirmation. */
|
|
325
|
+
deleting?: boolean;
|
|
326
|
+
/** True while the host is persisting the edit; locks the form. */
|
|
327
|
+
submitting?: boolean;
|
|
328
|
+
/** Server-reported field messages for the edit form, keyed by field name. */
|
|
329
|
+
fieldErrors?: Readonly<Record<string, string>>;
|
|
330
|
+
/** A form-level failure message from the host. */
|
|
331
|
+
error?: string | null;
|
|
332
|
+
onback?: () => void;
|
|
333
|
+
/** When set, an Edit action is offered (the host switches `mode`). */
|
|
334
|
+
onedit?: () => void;
|
|
335
|
+
/** Closes the edit form (Cancel, the sheet's close button, Escape). */
|
|
336
|
+
oncancel?: () => void;
|
|
337
|
+
/** Receives the edit form's wire-ready values; `EditFormProps['onsubmit']`. */
|
|
338
|
+
onsave?: EditFormProps['onsubmit'];
|
|
339
|
+
/** When set, Delete (with a confirmation) is offered in the overflow menu. */
|
|
340
|
+
ondelete?: () => void | Promise<void>;
|
|
341
|
+
/** Extra actions, rendered in the header's overflow menu before Delete. */
|
|
342
|
+
extraActions?: Snippet;
|
|
343
|
+
/**
|
|
344
|
+
* Badge tone per status value (`paid: 'success'`); unmapped values use
|
|
345
|
+
* `DEFAULT_STATUS_TONES`, else neutral.
|
|
346
|
+
*/
|
|
347
|
+
statusTones?: Readonly<Record<string, RecordStatusTone>>;
|
|
348
|
+
/** Extra content under the fields, inside the details card. */
|
|
349
|
+
children?: Snippet;
|
|
350
|
+
/**
|
|
351
|
+
* The edit form's derived (read-only, never submitted) fields. Defaults
|
|
352
|
+
* to the subtotal / discount / tax / total role fields when the model has
|
|
353
|
+
* an `items` relation, else none.
|
|
354
|
+
*/
|
|
355
|
+
derivedFields?: readonly string[];
|
|
356
|
+
/**
|
|
357
|
+
* Editable line items (the host's own editor), rendered under the edit
|
|
358
|
+
* form: in the edit column on desktop and inside the edit sheet on
|
|
359
|
+
* phones, so lines are editable on a phone too.
|
|
360
|
+
*/
|
|
361
|
+
items?: Snippet;
|
|
362
|
+
}
|
|
189
363
|
export interface RecipeScreensProps {
|
|
190
364
|
definition: ScreenCollectionDefinition;
|
|
191
365
|
/** Resolved field policy for the same object; omit for the code seed. */
|
|
@@ -211,5 +385,12 @@ export interface RecipeScreensProps {
|
|
|
211
385
|
};
|
|
212
386
|
/** Called on every user navigation, for router synchronization. */
|
|
213
387
|
onnavigate?: (view: RecipeScreenView, recordId: string | null) => void;
|
|
388
|
+
/**
|
|
389
|
+
* Every exposed collection definition, keyed by collection name; the
|
|
390
|
+
* record view derives this model's relations from it. Omit for none.
|
|
391
|
+
*/
|
|
392
|
+
definitions?: Readonly<Record<string, ScreenCollectionDefinition>>;
|
|
393
|
+
/** Passed to `RecordScreen` for the record view and edit. */
|
|
394
|
+
record?: Pick<RecordScreenProps, 'loaders' | 'registry' | 'widgetContext' | 'overview' | 'override' | 'onoverviewchange' | 'layoutEditing' | 'canCustomize' | 'hrefFor' | 'labels' | 'viewAllHref' | 'statusTones'>;
|
|
214
395
|
}
|
|
215
396
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/screens/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/screens/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -53,7 +53,29 @@ export interface FormatValueContext extends ScreenValueContext {
|
|
|
53
53
|
no?: string;
|
|
54
54
|
}
|
|
55
55
|
/** A record value as display text for a list cell or a view row. */
|
|
56
|
-
export declare function formatScreenValue(field: Pick<ScreenField, 'kind'
|
|
56
|
+
export declare function formatScreenValue(field: Pick<ScreenField, 'kind'> & {
|
|
57
|
+
definition?: Pick<ScreenField['definition'], 'format' | 'ui'>;
|
|
58
|
+
}, value: unknown, context?: FormatValueContext): string;
|
|
59
|
+
/** One row of a JSON object shown as a key/value list. */
|
|
60
|
+
export interface ScreenJsonEntry {
|
|
61
|
+
key: string;
|
|
62
|
+
value: string;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* A JSON object field as key/value rows for a record view (an address reads
|
|
66
|
+
* `Line1: 12 Main St`, not `{"line1":...}`): its non-blank top-level entries,
|
|
67
|
+
* nested values as compact JSON. `null` for anything that is not a non-empty
|
|
68
|
+
* plain object (an array, a scalar, stored text that is not an object), which
|
|
69
|
+
* the view shows through {@link formatScreenValue} instead; an empty object
|
|
70
|
+
* formats as `''`, the view's empty placeholder.
|
|
71
|
+
*/
|
|
72
|
+
export declare function jsonScreenEntries(value: unknown): ScreenJsonEntry[] | null;
|
|
73
|
+
/**
|
|
74
|
+
* A record id short enough to read where no label names the record: the
|
|
75
|
+
* first eight characters of a long id (a UUID's first group) and an
|
|
76
|
+
* ellipsis; a short id as is. Views keep the full id in a tooltip.
|
|
77
|
+
*/
|
|
78
|
+
export declare function shortScreenId(id: string): string;
|
|
57
79
|
/**
|
|
58
80
|
* Order two record values for a list column: empty values last (whichever
|
|
59
81
|
* way the column sorts), numbers numerically, everything else as text.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"values.d.ts","sourceRoot":"","sources":["../../../src/components/screens/values.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EACV,WAAW,EACX,gBAAgB,EAChB,WAAW,EACX,oBAAoB,EACpB,YAAY,EACZ,YAAY,EACb,MAAM,YAAY,CAAC;AAEpB,4EAA4E;AAC5E,eAAO,MAAM,uBAAuB,QAAQ,CAAC;AAE7C,yEAAyE;AACzE,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED,yEAAyE;AACzE,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,MAAM,GACb,MAAM,GAAG,IAAI,CAYf;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GACb,MAAM,GAAG,IAAI,CAUf;AAMD,wEAAwE;AACxE,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAMtD;AAED,MAAM,WAAW,kBAAkB;IACjC,0CAA0C;IAC1C,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,uEAAuE;AACvE,wBAAgB,YAAY,CAC1B,KAAK,EAAE,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,EAChC,KAAK,EAAE,OAAO,EACd,OAAO,GAAE,kBAAuB,GAC/B,gBAAgB,CAkBlB;AAED,4CAA4C;AAC5C,wBAAgB,eAAe,CAC7B,MAAM,EAAE,SAAS,WAAW,EAAE,EAC9B,MAAM,EAAE,YAAY,EACpB,OAAO,GAAE,kBAAuB,GAC/B,WAAW,CAMb;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,SAAS,WAAW,EAAE,EAC9B,MAAM,CAAC,EAAE,YAAY,GAAG,IAAI,EAC5B,OAAO,GAAE,kBAAuB,GAC/B,WAAW,CAWb;AAED,MAAM,WAAW,iBAAkB,SAAQ,kBAAkB;IAC3D;;;;;OAKG;IACH,MAAM,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;CAC9B;AAED,MAAM,WAAW,WAAW;IAC1B,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;CAC9C;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,SAAS,WAAW,EAAE,EAC9B,KAAK,EAAE,WAAW,EAClB,OAAO,GAAE,iBAAsB,GAC9B,WAAW,
|
|
1
|
+
{"version":3,"file":"values.d.ts","sourceRoot":"","sources":["../../../src/components/screens/values.ts"],"names":[],"mappings":"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"}
|
|
@@ -126,7 +126,8 @@ export function parseDraft(fields, draft, context = {}) {
|
|
|
126
126
|
field.kind === 'textarea' ||
|
|
127
127
|
field.kind === 'email' ||
|
|
128
128
|
field.kind === 'url' ||
|
|
129
|
-
field.kind === 'tel'
|
|
129
|
+
field.kind === 'tel' ||
|
|
130
|
+
field.kind === 'select') &&
|
|
130
131
|
field.definition.nullable !== true) {
|
|
131
132
|
values[field.name] = '';
|
|
132
133
|
}
|
|
@@ -203,6 +204,7 @@ export function parseDraft(fields, draft, context = {}) {
|
|
|
203
204
|
}
|
|
204
205
|
/** A record value as display text for a list cell or a view row. */
|
|
205
206
|
export function formatScreenValue(field, value, context = {}) {
|
|
207
|
+
const format = field.definition?.format ?? field.definition?.ui?.format;
|
|
206
208
|
if (field.kind === 'boolean') {
|
|
207
209
|
if (value === null || value === undefined)
|
|
208
210
|
return '';
|
|
@@ -221,6 +223,12 @@ export function formatScreenValue(field, value, context = {}) {
|
|
|
221
223
|
}).format(value)
|
|
222
224
|
: String(value);
|
|
223
225
|
case 'decimal':
|
|
226
|
+
if (format === 'percent' && typeof value === 'number') {
|
|
227
|
+
return new Intl.NumberFormat(context.locale, {
|
|
228
|
+
style: 'percent',
|
|
229
|
+
maximumFractionDigits: 4,
|
|
230
|
+
}).format(value);
|
|
231
|
+
}
|
|
224
232
|
return typeof value === 'number'
|
|
225
233
|
? new Intl.NumberFormat(context.locale, {
|
|
226
234
|
maximumFractionDigits: 6,
|
|
@@ -243,14 +251,19 @@ export function formatScreenValue(field, value, context = {}) {
|
|
|
243
251
|
const date = value instanceof Date ? value : new Date(value);
|
|
244
252
|
if (Number.isNaN(date.getTime()))
|
|
245
253
|
return String(value);
|
|
254
|
+
// A business date (`format: 'date'`) shows no time of day.
|
|
246
255
|
return new Intl.DateTimeFormat(context.locale, {
|
|
247
256
|
dateStyle: 'medium',
|
|
248
|
-
timeStyle: 'short',
|
|
257
|
+
...(format === 'date' ? {} : { timeStyle: 'short' }),
|
|
249
258
|
...(context.timeZone ? { timeZone: context.timeZone } : {}),
|
|
250
259
|
}).format(date);
|
|
251
260
|
}
|
|
252
|
-
case 'json':
|
|
253
|
-
|
|
261
|
+
case 'json': {
|
|
262
|
+
const parsed = parseJsonValue(value);
|
|
263
|
+
if (isEmptyJsonValue(parsed))
|
|
264
|
+
return '';
|
|
265
|
+
return typeof parsed === 'string' ? parsed : JSON.stringify(parsed);
|
|
266
|
+
}
|
|
254
267
|
default:
|
|
255
268
|
return String(value);
|
|
256
269
|
}
|
|
@@ -260,6 +273,69 @@ export function formatScreenValue(field, value, context = {}) {
|
|
|
260
273
|
return String(value);
|
|
261
274
|
}
|
|
262
275
|
}
|
|
276
|
+
/** A JSON field's value: stored JSON text parsed, anything else as given. */
|
|
277
|
+
function parseJsonValue(value) {
|
|
278
|
+
if (typeof value !== 'string')
|
|
279
|
+
return value;
|
|
280
|
+
const text = value.trim();
|
|
281
|
+
if (!text.startsWith('{') && !text.startsWith('['))
|
|
282
|
+
return value;
|
|
283
|
+
try {
|
|
284
|
+
return JSON.parse(text);
|
|
285
|
+
}
|
|
286
|
+
catch {
|
|
287
|
+
return value;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
function isPlainObject(value) {
|
|
291
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
292
|
+
}
|
|
293
|
+
function isBlank(value) {
|
|
294
|
+
return value === null || value === undefined || value === '';
|
|
295
|
+
}
|
|
296
|
+
/** `{}`, `[]`, or an object whose every value is blank: nothing to show. */
|
|
297
|
+
function isEmptyJsonValue(value) {
|
|
298
|
+
if (isBlank(value))
|
|
299
|
+
return true;
|
|
300
|
+
if (Array.isArray(value))
|
|
301
|
+
return value.length === 0;
|
|
302
|
+
if (isPlainObject(value))
|
|
303
|
+
return Object.values(value).every(isBlank);
|
|
304
|
+
return false;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* A JSON object field as key/value rows for a record view (an address reads
|
|
308
|
+
* `Line1: 12 Main St`, not `{"line1":...}`): its non-blank top-level entries,
|
|
309
|
+
* nested values as compact JSON. `null` for anything that is not a non-empty
|
|
310
|
+
* plain object (an array, a scalar, stored text that is not an object), which
|
|
311
|
+
* the view shows through {@link formatScreenValue} instead; an empty object
|
|
312
|
+
* formats as `''`, the view's empty placeholder.
|
|
313
|
+
*/
|
|
314
|
+
export function jsonScreenEntries(value) {
|
|
315
|
+
const parsed = parseJsonValue(value);
|
|
316
|
+
if (!isPlainObject(parsed))
|
|
317
|
+
return null;
|
|
318
|
+
const entries = [];
|
|
319
|
+
for (const [key, entry] of Object.entries(parsed)) {
|
|
320
|
+
if (isBlank(entry))
|
|
321
|
+
continue;
|
|
322
|
+
entries.push({
|
|
323
|
+
key,
|
|
324
|
+
value: entry !== null && typeof entry === 'object'
|
|
325
|
+
? JSON.stringify(entry)
|
|
326
|
+
: String(entry),
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
return entries.length > 0 ? entries : null;
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* A record id short enough to read where no label names the record: the
|
|
333
|
+
* first eight characters of a long id (a UUID's first group) and an
|
|
334
|
+
* ellipsis; a short id as is. Views keep the full id in a tooltip.
|
|
335
|
+
*/
|
|
336
|
+
export function shortScreenId(id) {
|
|
337
|
+
return id.length > 12 ? `${id.slice(0, 8)}…` : id;
|
|
338
|
+
}
|
|
263
339
|
/**
|
|
264
340
|
* Order two record values for a list column: empty values last (whichever
|
|
265
341
|
* way the column sorts), numbers numerically, everything else as text.
|