@happyvertical/smrt-svelte 0.55.22 → 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.
Files changed (85) hide show
  1. package/AGENTS.md +2 -2
  2. package/README.md +2 -2
  3. package/agents/overview-surfaces.md +108 -4
  4. package/agents/screens.md +171 -5
  5. package/dist/components/overview/index.d.ts +4 -1
  6. package/dist/components/overview/index.d.ts.map +1 -1
  7. package/dist/components/overview/index.js +3 -0
  8. package/dist/components/overview/record.d.ts +51 -0
  9. package/dist/components/overview/record.d.ts.map +1 -0
  10. package/dist/components/overview/record.js +146 -0
  11. package/dist/components/overview/server.d.ts +4 -0
  12. package/dist/components/overview/server.d.ts.map +1 -1
  13. package/dist/components/overview/server.js +4 -0
  14. package/dist/components/overview/types.d.ts +12 -0
  15. package/dist/components/overview/types.d.ts.map +1 -1
  16. package/dist/components/overview/widgets/LineItemsWidget.svelte +144 -0
  17. package/dist/components/overview/widgets/LineItemsWidget.svelte.d.ts +6 -0
  18. package/dist/components/overview/widgets/LineItemsWidget.svelte.d.ts.map +1 -0
  19. package/dist/components/overview/widgets/RecordListWidget.svelte +67 -3
  20. package/dist/components/overview/widgets/RecordListWidget.svelte.d.ts.map +1 -1
  21. package/dist/components/overview/widgets/RecordTable.svelte +167 -0
  22. package/dist/components/overview/widgets/RecordTable.svelte.d.ts +21 -0
  23. package/dist/components/overview/widgets/RecordTable.svelte.d.ts.map +1 -0
  24. package/dist/components/overview/widgets/RelatedWidget.svelte +117 -0
  25. package/dist/components/overview/widgets/RelatedWidget.svelte.d.ts +6 -0
  26. package/dist/components/overview/widgets/RelatedWidget.svelte.d.ts.map +1 -0
  27. package/dist/components/overview/widgets/core.d.ts +13 -2
  28. package/dist/components/overview/widgets/core.d.ts.map +1 -1
  29. package/dist/components/overview/widgets/core.js +43 -2
  30. package/dist/components/overview/widgets/data.d.ts +97 -8
  31. package/dist/components/overview/widgets/data.d.ts.map +1 -1
  32. package/dist/components/overview/widgets/line-totals.d.ts +48 -0
  33. package/dist/components/overview/widgets/line-totals.d.ts.map +1 -0
  34. package/dist/components/overview/widgets/line-totals.js +91 -0
  35. package/dist/components/overview/widgets/record-loaders.d.ts +63 -0
  36. package/dist/components/overview/widgets/record-loaders.d.ts.map +1 -0
  37. package/dist/components/overview/widgets/record-loaders.js +150 -0
  38. package/dist/components/overview/widgets/record-table.d.ts +45 -0
  39. package/dist/components/overview/widgets/record-table.d.ts.map +1 -0
  40. package/dist/components/overview/widgets/record-table.js +201 -0
  41. package/dist/components/overview/widgets/record-widgets.d.ts +27 -0
  42. package/dist/components/overview/widgets/record-widgets.d.ts.map +1 -0
  43. package/dist/components/overview/widgets/record-widgets.js +100 -0
  44. package/dist/components/screens/DetailScreen.svelte +105 -6
  45. package/dist/components/screens/DetailScreen.svelte.d.ts.map +1 -1
  46. package/dist/components/screens/EditForm.svelte +39 -3
  47. package/dist/components/screens/EditForm.svelte.d.ts.map +1 -1
  48. package/dist/components/screens/RecipeScreens.svelte +18 -18
  49. package/dist/components/screens/RecipeScreens.svelte.d.ts.map +1 -1
  50. package/dist/components/screens/RecordHeader.svelte +238 -0
  51. package/dist/components/screens/RecordHeader.svelte.d.ts +26 -0
  52. package/dist/components/screens/RecordHeader.svelte.d.ts.map +1 -0
  53. package/dist/components/screens/RecordScreen.svelte +511 -0
  54. package/dist/components/screens/RecordScreen.svelte.d.ts +5 -0
  55. package/dist/components/screens/RecordScreen.svelte.d.ts.map +1 -0
  56. package/dist/components/screens/columns.d.ts +29 -0
  57. package/dist/components/screens/columns.d.ts.map +1 -0
  58. package/dist/components/screens/columns.js +115 -0
  59. package/dist/components/screens/fields.d.ts +12 -2
  60. package/dist/components/screens/fields.d.ts.map +1 -1
  61. package/dist/components/screens/fields.js +37 -5
  62. package/dist/components/screens/index.d.ts +7 -2
  63. package/dist/components/screens/index.d.ts.map +1 -1
  64. package/dist/components/screens/index.js +6 -1
  65. package/dist/components/screens/record-header.d.ts +51 -0
  66. package/dist/components/screens/record-header.d.ts.map +1 -0
  67. package/dist/components/screens/record-header.js +118 -0
  68. package/dist/components/screens/relations.d.ts +49 -0
  69. package/dist/components/screens/relations.d.ts.map +1 -0
  70. package/dist/components/screens/relations.js +98 -0
  71. package/dist/components/screens/roles.d.ts +56 -0
  72. package/dist/components/screens/roles.d.ts.map +1 -0
  73. package/dist/components/screens/roles.js +174 -0
  74. package/dist/components/screens/types.d.ts +182 -1
  75. package/dist/components/screens/types.d.ts.map +1 -1
  76. package/dist/components/screens/values.d.ts +23 -1
  77. package/dist/components/screens/values.d.ts.map +1 -1
  78. package/dist/components/screens/values.js +80 -4
  79. package/dist/i18n/strings.overview.d.ts +25 -0
  80. package/dist/i18n/strings.overview.d.ts.map +1 -1
  81. package/dist/i18n/strings.overview.js +25 -0
  82. package/dist/i18n/strings.screens.d.ts +8 -0
  83. package/dist/i18n/strings.screens.d.ts.map +1 -1
  84. package/dist/i18n/strings.screens.js +8 -0
  85. package/package.json +16 -16
@@ -10,6 +10,7 @@
10
10
  * `basic` markers anywhere => everything basic; any marker => unmarked fields
11
11
  * advanced).
12
12
  */
13
+ import { type FieldRole } from './roles.js';
13
14
  import type { ScreenCollectionDefinition, ScreenField, ScreenFieldDefinition, ScreenInputKind, ScreenMode, ScreenPolicy } from './types.js';
14
15
  /** Columns shown by default on a list when the policy marks many basic. */
15
16
  export declare const DEFAULT_MAX_LIST_COLUMNS = 6;
@@ -27,13 +28,22 @@ export interface DeriveScreenFieldsOptions {
27
28
  }
28
29
  /** `dueDate` -> `Due date`; reference fields drop a trailing `Id`. */
29
30
  export declare function humanizeFieldName(name: string): string;
31
+ /** Default label of a field: reference fields drop a trailing `Id`. */
32
+ export declare function humanizeLabel(name: string, definition: ScreenFieldDefinition): string;
30
33
  /** Singular and plural display names derived from the class name. */
31
34
  export declare function screenTitles(definition: ScreenCollectionDefinition): {
32
35
  singular: string;
33
36
  plural: string;
34
37
  };
35
- /** Which control renders a field, from its widget hint and wire type. */
36
- export declare function inputKindFor(definition: ScreenFieldDefinition): ScreenInputKind;
38
+ /**
39
+ * Which control renders a field, from its wire type, its `enum`, its widget
40
+ * hint and its roles (`deriveFieldRoles`): a text field with an `enum` is a
41
+ * `select`; an INTEGER field with the `money` role (an explicit role, a
42
+ * `format: 'money'` hint, the legacy `ui.widget: 'currency'`, or a money
43
+ * name like `totalAmount`) is `money`. Without `roles`, only the
44
+ * definition's own hints count.
45
+ */
46
+ export declare function inputKindFor(definition: ScreenFieldDefinition, roles?: readonly FieldRole[]): ScreenInputKind;
37
47
  /**
38
48
  * Mismatch between a definition and a policy, or `null` when they belong
39
49
  * together. Components surface this instead of rendering the wrong object.
@@ -1 +1 @@
1
- {"version":3,"file":"fields.d.ts","sourceRoot":"","sources":["../../../src/components/screens/fields.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EACV,0BAA0B,EAC1B,WAAW,EACX,qBAAqB,EACrB,eAAe,EACf,UAAU,EACV,YAAY,EACb,MAAM,YAAY,CAAC;AAepB,2EAA2E;AAC3E,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAE1C,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,UAAU,CAAC;IACjB;;;OAGG;IACH,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,2BAA2B;IAC3B,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,0EAA0E;IAC1E,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,sEAAsE;AACtE,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAStD;AAaD,qEAAqE;AACrE,wBAAgB,YAAY,CAAC,UAAU,EAAE,0BAA0B,GAAG;IACpE,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB,CAWA;AAED,yEAAyE;AACzE,wBAAgB,YAAY,CAC1B,UAAU,EAAE,qBAAqB,GAChC,eAAe,CA0BjB;AAUD;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,0BAA0B,EACtC,MAAM,EAAE,YAAY,GAAG,SAAS,GAAG,IAAI,GACtC,MAAM,GAAG,IAAI,CAKf;AAiCD;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,0BAA0B,EACtC,MAAM,EAAE,YAAY,GAAG,SAAS,GAAG,IAAI,EACvC,OAAO,EAAE,yBAAyB,GACjC,WAAW,EAAE,CA6Gf;AAED,2EAA2E;AAC3E,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,SAAS,WAAW,EAAE,GAC7B,KAAK,CAAC,CAAC,MAAM,GAAG,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC,CAQvC"}
1
+ {"version":3,"file":"fields.d.ts","sourceRoot":"","sources":["../../../src/components/screens/fields.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAC9D,OAAO,KAAK,EACV,0BAA0B,EAC1B,WAAW,EACX,qBAAqB,EACrB,eAAe,EACf,UAAU,EACV,YAAY,EACb,MAAM,YAAY,CAAC;AAepB,2EAA2E;AAC3E,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAE1C,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,UAAU,CAAC;IACjB;;;OAGG;IACH,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,2BAA2B;IAC3B,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,0EAA0E;IAC1E,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,sEAAsE;AACtE,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAStD;AAED,uEAAuE;AACvE,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,qBAAqB,GAChC,MAAM,CASR;AAED,qEAAqE;AACrE,wBAAgB,YAAY,CAAC,UAAU,EAAE,0BAA0B,GAAG;IACpE,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB,CAWA;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC1B,UAAU,EAAE,qBAAqB,EACjC,KAAK,CAAC,EAAE,SAAS,SAAS,EAAE,GAC3B,eAAe,CAgCjB;AAUD;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,0BAA0B,EACtC,MAAM,EAAE,YAAY,GAAG,SAAS,GAAG,IAAI,GACtC,MAAM,GAAG,IAAI,CAKf;AAiCD;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,0BAA0B,EACtC,MAAM,EAAE,YAAY,GAAG,SAAS,GAAG,IAAI,EACvC,OAAO,EAAE,yBAAyB,GACjC,WAAW,EAAE,CAmHf;AAED,2EAA2E;AAC3E,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,SAAS,WAAW,EAAE,GAC7B,KAAK,CAAC,CAAC,MAAM,GAAG,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC,CAQvC"}
@@ -1,3 +1,16 @@
1
+ /**
2
+ * Pure derivation of list / view / edit fields from a manifest-derived
3
+ * collection definition and its resolved field policy (#3718).
4
+ *
5
+ * Mirrors smrt-fields' `ObjectForm` selection rule: a field renders only when
6
+ * it is in BOTH the generated web definition and the resolved policy, so
7
+ * sensitive, transient and read-permission-gated fields (which the policy
8
+ * resolver omits) can never appear on a generated screen. Without a policy the
9
+ * code seed applies: the manifest `ui` hints and the cold-start rule (no
10
+ * `basic` markers anywhere => everything basic; any marker => unmarked fields
11
+ * advanced).
12
+ */
13
+ import { deriveFieldRoles } from './roles.js';
1
14
  /** Managed by the platform; never editable and never a default column. */
2
15
  const SYSTEM_FIELDS = new Set([
3
16
  'id',
@@ -23,7 +36,8 @@ export function humanizeFieldName(name) {
23
36
  const lower = spaced.toLowerCase();
24
37
  return lower.charAt(0).toUpperCase() + lower.slice(1);
25
38
  }
26
- function humanizeLabel(name, definition) {
39
+ /** Default label of a field: reference fields drop a trailing `Id`. */
40
+ export function humanizeLabel(name, definition) {
27
41
  if ((definition.type === 'foreignKey' ||
28
42
  definition.type === 'crossPackageRef') &&
29
43
  /.[a-z]Id$/.test(name)) {
@@ -44,10 +58,19 @@ export function screenTitles(definition) {
44
58
  : `${singular}s`;
45
59
  return { singular, plural };
46
60
  }
47
- /** Which control renders a field, from its widget hint and wire type. */
48
- export function inputKindFor(definition) {
61
+ /**
62
+ * Which control renders a field, from its wire type, its `enum`, its widget
63
+ * hint and its roles (`deriveFieldRoles`): a text field with an `enum` is a
64
+ * `select`; an INTEGER field with the `money` role (an explicit role, a
65
+ * `format: 'money'` hint, the legacy `ui.widget: 'currency'`, or a money
66
+ * name like `totalAmount`) is `money`. Without `roles`, only the
67
+ * definition's own hints count.
68
+ */
69
+ export function inputKindFor(definition, roles) {
49
70
  const widget = definition.ui?.widget;
50
71
  if (definition.type === 'text') {
72
+ if (Array.isArray(definition.enum) && definition.enum.length > 0)
73
+ return 'select';
51
74
  if (widget === 'textarea')
52
75
  return 'textarea';
53
76
  if (widget === 'email')
@@ -61,7 +84,10 @@ export function inputKindFor(definition) {
61
84
  // Money is integer minor units only; a currency hint on a decimal is a
62
85
  // plain decimal (rates and confidences are the only decimals).
63
86
  if (definition.type === 'integer') {
64
- return widget === 'currency' ? 'money' : 'integer';
87
+ const money = roles?.includes('money') ??
88
+ (widget === 'currency' ||
89
+ (definition.format ?? definition.ui?.format) === 'money');
90
+ return money ? 'money' : 'integer';
65
91
  }
66
92
  switch (definition.type) {
67
93
  case 'decimal':
@@ -132,6 +158,7 @@ export function deriveScreenFields(definition, policy, options) {
132
158
  !SYSTEM_FIELDS.has(c.name) &&
133
159
  c.name !== definition.idField);
134
160
  const seedBasic = policy ? null : seedBasicSet(candidates);
161
+ const roles = deriveFieldRoles(definition);
135
162
  const decorate = (c) => {
136
163
  const policyField = policy?.fields[c.name];
137
164
  if (policy && !policyField)
@@ -145,17 +172,21 @@ export function deriveScreenFields(definition, policy, options) {
145
172
  return null;
146
173
  const label = policyField?.label?.trim();
147
174
  const help = policyField?.help ?? c.definition.description ?? null;
175
+ const fieldRoles = roles[c.name] ?? [];
176
+ const kind = inputKindFor(c.definition, fieldRoles);
148
177
  return {
149
178
  name: c.name,
150
179
  label: label || humanizeLabel(c.name, c.definition),
151
180
  help: help && help.trim() !== '' ? help : null,
152
181
  type: c.definition.type,
153
- kind: inputKindFor(c.definition),
182
+ kind,
154
183
  required: isRequired(c.definition, policyField?.required),
155
184
  tier,
156
185
  group: policyField?.group ?? c.definition.ui?.group ?? null,
157
186
  order: policyField?.order ?? c.definition.ui?.order ?? null,
158
187
  definition: c.definition,
188
+ roles: fieldRoles,
189
+ ...(kind === 'select' ? { options: [...(c.definition.enum ?? [])] } : {}),
159
190
  };
160
191
  };
161
192
  const decorated = candidates
@@ -177,6 +208,7 @@ export function deriveScreenFields(definition, policy, options) {
177
208
  group: null,
178
209
  order: null,
179
210
  definition: fieldDefinition,
211
+ roles: roles[name] ?? [],
180
212
  };
181
213
  };
182
214
  if (include) {
@@ -1,8 +1,13 @@
1
+ export { lineItemColumns, relatedColumns } from './columns.js';
1
2
  export { default as DetailScreen } from './DetailScreen.svelte';
2
3
  export { default as EditForm } from './EditForm.svelte';
3
4
  export { DEFAULT_MAX_LIST_COLUMNS, type DeriveScreenFieldsOptions, deriveScreenFields, groupScreenFields, humanizeFieldName, inputKindFor, screenSourceError, screenTitles, } from './fields.js';
4
5
  export { default as ListScreen } from './ListScreen.svelte';
5
6
  export { default as RecipeScreens } from './RecipeScreens.svelte';
6
- export type { DetailScreenProps, EditFormProps, EditFormSubmitResult, ListScreenProps, RecipeScreensProps, RecipeScreensSource, RecipeScreenView, ScreenCollectionDefinition, ScreenDraft, ScreenDraftValue, ScreenField, ScreenFieldDefinition, ScreenFieldErrorCode, ScreenFieldPolicy, ScreenFieldType, ScreenFieldUIHints, ScreenFieldVisibility, ScreenInputKind, ScreenMode, ScreenPolicy, ScreenRecord, } from './types.js';
7
- export { compareScreenValues, currencyMinorDigits, DEFAULT_SCREEN_CURRENCY, draftForCreate, draftFromRecord, type FormatValueContext, formatScreenValue, majorStringToMinorUnits, minorUnitsToMajorString, type ParsedDraft, parseDraft, toDatetimeLocal, toDraftValue, } from './values.js';
7
+ export { default as RecordScreen } from './RecordScreen.svelte';
8
+ export { deriveRecordHeader, RECORD_HEADER_MAX_FACTS, type RecordFact, type RecordFactKind, type RecordHeaderModel, recordTotals, totalsFields, } from './record-header.js';
9
+ export { deriveScreenRelations, relationKey, type ScreenRelation, } from './relations.js';
10
+ export { currencyCodeField, deriveFieldRoles, FIELD_ROLES, type FieldRole, fieldsWithRole, fieldWithRole, hasLineItemRoles, isDocumentLike, type ScreenFieldRoles, } from './roles.js';
11
+ export type { DetailScreenProps, EditFormProps, EditFormSubmitResult, ListScreenProps, RecipeScreensProps, RecipeScreensSource, RecipeScreenView, RecordScreenMode, RecordScreenProps, ScreenCollectionDefinition, ScreenDraft, ScreenDraftValue, ScreenField, ScreenFieldDefinition, ScreenFieldErrorCode, ScreenFieldFormat, ScreenFieldPolicy, ScreenFieldType, ScreenFieldUIHints, ScreenFieldVisibility, ScreenInputKind, ScreenMode, ScreenPolicy, ScreenRecord, ScreenRelationshipDefinition, ScreenRelationshipKind, } from './types.js';
12
+ export { compareScreenValues, currencyMinorDigits, DEFAULT_SCREEN_CURRENCY, draftForCreate, draftFromRecord, type FormatValueContext, formatScreenValue, jsonScreenEntries, majorStringToMinorUnits, minorUnitsToMajorString, type ParsedDraft, parseDraft, type ScreenJsonEntry, shortScreenId, toDatetimeLocal, toDraftValue, } from './values.js';
8
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/screens/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EACL,wBAAwB,EACxB,KAAK,yBAAyB,EAC9B,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,YAAY,EACZ,iBAAiB,EACjB,YAAY,GACb,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAClE,YAAY,EACV,iBAAiB,EACjB,aAAa,EACb,oBAAoB,EACpB,eAAe,EACf,kBAAkB,EAClB,mBAAmB,EACnB,gBAAgB,EAChB,0BAA0B,EAC1B,WAAW,EACX,gBAAgB,EAChB,WAAW,EACX,qBAAqB,EACrB,oBAAoB,EACpB,iBAAiB,EACjB,eAAe,EACf,kBAAkB,EAClB,qBAAqB,EACrB,eAAe,EACf,UAAU,EACV,YAAY,EACZ,YAAY,GACb,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,uBAAuB,EACvB,cAAc,EACd,eAAe,EACf,KAAK,kBAAkB,EACvB,iBAAiB,EACjB,uBAAuB,EACvB,uBAAuB,EACvB,KAAK,WAAW,EAChB,UAAU,EACV,eAAe,EACf,YAAY,GACb,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/screens/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EACL,wBAAwB,EACxB,KAAK,yBAAyB,EAC9B,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,YAAY,EACZ,iBAAiB,EACjB,YAAY,GACb,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EACL,kBAAkB,EAClB,uBAAuB,EACvB,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,YAAY,EACZ,YAAY,GACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,qBAAqB,EACrB,WAAW,EACX,KAAK,cAAc,GACpB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,WAAW,EACX,KAAK,SAAS,EACd,cAAc,EACd,aAAa,EACb,gBAAgB,EAChB,cAAc,EACd,KAAK,gBAAgB,GACtB,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,iBAAiB,EACjB,aAAa,EACb,oBAAoB,EACpB,eAAe,EACf,kBAAkB,EAClB,mBAAmB,EACnB,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,0BAA0B,EAC1B,WAAW,EACX,gBAAgB,EAChB,WAAW,EACX,qBAAqB,EACrB,oBAAoB,EACpB,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EACf,kBAAkB,EAClB,qBAAqB,EACrB,eAAe,EACf,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,4BAA4B,EAC5B,sBAAsB,GACvB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,uBAAuB,EACvB,cAAc,EACd,eAAe,EACf,KAAK,kBAAkB,EACvB,iBAAiB,EACjB,iBAAiB,EACjB,uBAAuB,EACvB,uBAAuB,EACvB,KAAK,WAAW,EAChB,UAAU,EACV,KAAK,eAAe,EACpB,aAAa,EACb,eAAe,EACf,YAAY,GACb,MAAM,aAAa,CAAC"}
@@ -1,6 +1,11 @@
1
+ export { lineItemColumns, relatedColumns } from './columns.js';
1
2
  export { default as DetailScreen } from './DetailScreen.svelte';
2
3
  export { default as EditForm } from './EditForm.svelte';
3
4
  export { DEFAULT_MAX_LIST_COLUMNS, deriveScreenFields, groupScreenFields, humanizeFieldName, inputKindFor, screenSourceError, screenTitles, } from './fields.js';
4
5
  export { default as ListScreen } from './ListScreen.svelte';
5
6
  export { default as RecipeScreens } from './RecipeScreens.svelte';
6
- export { compareScreenValues, currencyMinorDigits, DEFAULT_SCREEN_CURRENCY, draftForCreate, draftFromRecord, formatScreenValue, majorStringToMinorUnits, minorUnitsToMajorString, parseDraft, toDatetimeLocal, toDraftValue, } from './values.js';
7
+ export { default as RecordScreen } from './RecordScreen.svelte';
8
+ export { deriveRecordHeader, RECORD_HEADER_MAX_FACTS, recordTotals, totalsFields, } from './record-header.js';
9
+ export { deriveScreenRelations, relationKey, } from './relations.js';
10
+ export { currencyCodeField, deriveFieldRoles, FIELD_ROLES, fieldsWithRole, fieldWithRole, hasLineItemRoles, isDocumentLike, } from './roles.js';
11
+ export { compareScreenValues, currencyMinorDigits, DEFAULT_SCREEN_CURRENCY, draftForCreate, draftFromRecord, formatScreenValue, jsonScreenEntries, majorStringToMinorUnits, minorUnitsToMajorString, parseDraft, shortScreenId, toDatetimeLocal, toDraftValue, } from './values.js';
@@ -0,0 +1,51 @@
1
+ import { type ScreenFieldRoles } from './roles.js';
2
+ import type { ScreenCollectionDefinition, ScreenField, ScreenRecord } from './types.js';
3
+ export type RecordFactKind = 'party' | 'date' | 'money' | 'contact';
4
+ export interface RecordFact {
5
+ kind: RecordFactKind;
6
+ field: ScreenField;
7
+ }
8
+ export interface RecordHeaderModel {
9
+ heading: string;
10
+ /** The heading leads with the identifier (a document): render it monospace there. */
11
+ headingIdentifier: boolean;
12
+ identifier?: {
13
+ field: ScreenField;
14
+ value: string;
15
+ };
16
+ status?: {
17
+ field: ScreenField;
18
+ value: string;
19
+ };
20
+ facts: RecordFact[];
21
+ /** Fields the header shows; the details card leaves them out. */
22
+ shown: string[];
23
+ }
24
+ /** Most key facts in a header. */
25
+ export declare const RECORD_HEADER_MAX_FACTS = 4;
26
+ /**
27
+ * The header of `record`: `fields` are the view fields
28
+ * (`deriveScreenFields(..., { mode: 'view' })`), `roles` the definition's
29
+ * (`deriveFieldRoles`).
30
+ */
31
+ export declare function deriveRecordHeader(definition: ScreenCollectionDefinition, fields: readonly ScreenField[], record: ScreenRecord, roles: ScreenFieldRoles, options?: {
32
+ title?: string;
33
+ maxFacts?: number;
34
+ }): RecordHeaderModel;
35
+ /**
36
+ * The record's own totals, read by role (`subtotal`, `discount`, `tax`,
37
+ * `total`): what the line-items totals block shows as authoritative. With
38
+ * `visible` (the policy-filtered view fields), a field the policy omits is
39
+ * never read.
40
+ */
41
+ export declare function recordTotals(record: ScreenRecord, roles: ScreenFieldRoles, visible?: readonly {
42
+ name: string;
43
+ }[]): {
44
+ subtotal?: number | null;
45
+ discount?: number | null;
46
+ tax?: number | null;
47
+ total?: number | null;
48
+ };
49
+ /** Fields holding a totals role (`subtotal` / `discount` / `tax` / `total`). */
50
+ export declare function totalsFields(roles: ScreenFieldRoles): string[];
51
+ //# sourceMappingURL=record-header.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"record-header.d.ts","sourceRoot":"","sources":["../../../src/components/screens/record-header.ts"],"names":[],"mappings":"AAcA,OAAO,EAAkB,KAAK,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACnE,OAAO,KAAK,EACV,0BAA0B,EAC1B,WAAW,EACX,YAAY,EACb,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;AAEpE,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,cAAc,CAAC;IACrB,KAAK,EAAE,WAAW,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,qFAAqF;IACrF,iBAAiB,EAAE,OAAO,CAAC;IAC3B,UAAU,CAAC,EAAE;QAAE,KAAK,EAAE,WAAW,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACnD,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,WAAW,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,iEAAiE;IACjE,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,kCAAkC;AAClC,eAAO,MAAM,uBAAuB,IAAI,CAAC;AAYzC;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,0BAA0B,EACtC,MAAM,EAAE,SAAS,WAAW,EAAE,EAC9B,MAAM,EAAE,YAAY,EACpB,KAAK,EAAE,gBAAgB,EACvB,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAClD,iBAAiB,CAyEnB;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,YAAY,EACpB,KAAK,EAAE,gBAAgB,EACvB,OAAO,CAAC,EAAE,SAAS;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,GACpC;IACD,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,CAcA;AAED,gFAAgF;AAChF,wBAAgB,YAAY,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM,EAAE,CAM9D"}
@@ -0,0 +1,118 @@
1
+ /**
2
+ * The record page header (#3718 record page), pure and Node-tested: the
3
+ * heading, the monospace identifier, the status and two to four key facts,
4
+ * all read by role from the fields the screen may show (so a field the
5
+ * policy omits can never reach the header).
6
+ *
7
+ * - Heading: the `title` prop, else a document's kind and identifier
8
+ * ("Order GRA-3159", never the customer), else the title-role field, else
9
+ * the identifier, else the class name.
10
+ * - Facts, in order: the party (a link), the lead date, the total money (the
11
+ * `total` role, else the first money field), then a contact (email, then
12
+ * phone) while there is room. Empty values are skipped.
13
+ */
14
+ import { screenTitles } from './fields.js';
15
+ import { isDocumentLike } from './roles.js';
16
+ /** Most key facts in a header. */
17
+ export const RECORD_HEADER_MAX_FACTS = 4;
18
+ function present(value) {
19
+ if (value === null || value === undefined)
20
+ return false;
21
+ if (typeof value === 'string')
22
+ return value.trim() !== '';
23
+ return true;
24
+ }
25
+ function hasRole(field, role) {
26
+ return (field.roles ?? []).includes(role);
27
+ }
28
+ /**
29
+ * The header of `record`: `fields` are the view fields
30
+ * (`deriveScreenFields(..., { mode: 'view' })`), `roles` the definition's
31
+ * (`deriveFieldRoles`).
32
+ */
33
+ export function deriveRecordHeader(definition, fields, record, roles, options = {}) {
34
+ const { singular } = screenTitles(definition);
35
+ const filled = fields.filter((f) => present(record[f.name]));
36
+ const find = (role) => filled.find((f) => hasRole(f, role));
37
+ const shown = [];
38
+ const titleField = find('title');
39
+ const identifierField = find('identifier');
40
+ const statusField = find('status');
41
+ const identifierValue = identifierField
42
+ ? String(record[identifierField.name])
43
+ : undefined;
44
+ let heading;
45
+ let headingIdentifier = false;
46
+ if (options.title) {
47
+ heading = options.title;
48
+ }
49
+ else if (identifierValue && isDocumentLike(roles)) {
50
+ heading = `${singular} ${identifierValue}`;
51
+ headingIdentifier = true;
52
+ }
53
+ else if (titleField) {
54
+ heading = String(record[titleField.name]);
55
+ }
56
+ else if (identifierValue) {
57
+ heading = identifierValue;
58
+ headingIdentifier = true;
59
+ }
60
+ else {
61
+ heading = singular;
62
+ }
63
+ if (titleField && !headingIdentifier && !options.title)
64
+ shown.push(titleField.name);
65
+ const out = {
66
+ heading,
67
+ headingIdentifier,
68
+ facts: [],
69
+ shown,
70
+ };
71
+ if (identifierField && identifierValue) {
72
+ out.identifier = { field: identifierField, value: identifierValue };
73
+ shown.push(identifierField.name);
74
+ }
75
+ if (statusField) {
76
+ out.status = {
77
+ field: statusField,
78
+ value: String(record[statusField.name]),
79
+ };
80
+ shown.push(statusField.name);
81
+ }
82
+ const max = options.maxFacts ?? RECORD_HEADER_MAX_FACTS;
83
+ const add = (kind, field) => {
84
+ if (!field || shown.includes(field.name) || out.facts.length >= max)
85
+ return;
86
+ out.facts.push({ kind, field });
87
+ shown.push(field.name);
88
+ };
89
+ add('party', find('party'));
90
+ add('date', filled.find((f) => hasRole(f, 'date') && f.name !== 'createdAt' && f.name !== 'updatedAt'));
91
+ add('money', find('total') ?? find('money'));
92
+ add('contact', filled.find((f) => f.kind === 'email'));
93
+ add('contact', filled.find((f) => f.kind === 'tel'));
94
+ return out;
95
+ }
96
+ /**
97
+ * The record's own totals, read by role (`subtotal`, `discount`, `tax`,
98
+ * `total`): what the line-items totals block shows as authoritative. With
99
+ * `visible` (the policy-filtered view fields), a field the policy omits is
100
+ * never read.
101
+ */
102
+ export function recordTotals(record, roles, visible) {
103
+ const out = {};
104
+ for (const role of ['subtotal', 'discount', 'tax', 'total']) {
105
+ const field = Object.keys(roles).find((name) => roles[name]?.includes(role) &&
106
+ (!visible || visible.some((f) => f.name === name)));
107
+ if (!field)
108
+ continue;
109
+ const value = record[field];
110
+ out[role] =
111
+ typeof value === 'number' && Number.isSafeInteger(value) ? value : null;
112
+ }
113
+ return out;
114
+ }
115
+ /** Fields holding a totals role (`subtotal` / `discount` / `tax` / `total`). */
116
+ export function totalsFields(roles) {
117
+ return Object.keys(roles).filter((name) => ['subtotal', 'discount', 'tax', 'total'].some((role) => roles[name]?.includes(role)));
118
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Pure derivation of a model's relations from the generated web collection
3
+ * definitions (#3718 record view).
4
+ *
5
+ * A `children` relation is another collection whose `foreignKey` /
6
+ * `crossPackageRef` edge targets this model (a tab of the record page's
7
+ * related records); an `items` relation is a `children` one whose rows are
8
+ * this record's line items (the line-items table); a `reference` relation is one of this model's
9
+ * own FK fields (rendered as a link on the detail). Edges whose target or
10
+ * source is not in `definitions` are skipped: an unexposed model has neither
11
+ * a definition nor a route, so nothing could be linked.
12
+ */
13
+ import type { RecordListColumn } from '../overview/widgets/data.js';
14
+ import type { ScreenCollectionDefinition } from './types.js';
15
+ export interface ScreenRelation {
16
+ /** Qualified model id (`objectRef`) of the child (`children`) or target (`reference`). */
17
+ model: string;
18
+ /** FK field name on the child (`children`) or on this model (`reference`). */
19
+ field: string;
20
+ label: string;
21
+ /**
22
+ * `items` is a `children` relation whose children are this record's line
23
+ * items: the edge says so (`relationships[].role: 'items'`), or the child
24
+ * has line-item roles (a quantity and a money field).
25
+ */
26
+ kind: 'children' | 'reference' | 'items';
27
+ /**
28
+ * `children` / `items` only: the table columns for the child model, chosen
29
+ * by role (`relatedColumns`; `lineItemColumns` for items), the FK back to
30
+ * this model excluded.
31
+ */
32
+ columns?: readonly RecordListColumn[];
33
+ /** `items` only: the child field lines are ordered by (`sortOrder`, ..., else `createdAt`). */
34
+ sort?: string;
35
+ }
36
+ /**
37
+ * A relation's stable key, `<Class>-<field>` (`Order-customerId`): what the
38
+ * `related` widget's `relations` option lists and default widget ids end in.
39
+ */
40
+ export declare function relationKey(relation: Pick<ScreenRelation, 'model' | 'field'>): string;
41
+ /**
42
+ * Relations of `model` (a qualified `objectRef`, or a key of `definitions`).
43
+ * `items` come first, then `children`, each sorted by label; `reference`
44
+ * relations follow in declaration order. A child with two FKs to the same
45
+ * parent yields two relations, labelled `Orders (Owner)` /
46
+ * `Orders (Approver)` (and is only `items` when an edge says so).
47
+ */
48
+ export declare function deriveScreenRelations(definitions: Readonly<Record<string, ScreenCollectionDefinition>>, model: string): ScreenRelation[];
49
+ //# sourceMappingURL=relations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relations.d.ts","sourceRoot":"","sources":["../../../src/components/screens/relations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAIpE,OAAO,KAAK,EACV,0BAA0B,EAE3B,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,cAAc;IAC7B,0FAA0F;IAC1F,KAAK,EAAE,MAAM,CAAC;IACd,8EAA8E;IAC9E,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,IAAI,EAAE,UAAU,GAAG,WAAW,GAAG,OAAO,CAAC;IACzC;;;;OAIG;IACH,OAAO,CAAC,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACtC,+FAA+F;IAC/F,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAID;;;GAGG;AACH,wBAAgB,WAAW,CACzB,QAAQ,EAAE,IAAI,CAAC,cAAc,EAAE,OAAO,GAAG,OAAO,CAAC,GAChD,MAAM,CAKR;AAyBD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,0BAA0B,CAAC,CAAC,EACjE,KAAK,EAAE,MAAM,GACZ,cAAc,EAAE,CAqElB"}
@@ -0,0 +1,98 @@
1
+ import { lineItemColumns, relatedColumns } from './columns.js';
2
+ import { humanizeLabel, screenTitles } from './fields.js';
3
+ import { deriveFieldRoles, hasLineItemRoles } from './roles.js';
4
+ const LINE_ORDER_FIELDS = ['sortOrder', 'position', 'lineNumber', 'sequence'];
5
+ /**
6
+ * A relation's stable key, `<Class>-<field>` (`Order-customerId`): what the
7
+ * `related` widget's `relations` option lists and default widget ids end in.
8
+ */
9
+ export function relationKey(relation) {
10
+ const className = relation.model.split(/[:/]/).pop() ?? relation.model;
11
+ return `${className}-${relation.field}`
12
+ .replace(/[^A-Za-z0-9_.-]+/g, '-')
13
+ .slice(0, 64);
14
+ }
15
+ const FK_KINDS = new Set([
16
+ 'foreignKey',
17
+ 'crossPackageRef',
18
+ ]);
19
+ function fieldLabel(field, definition) {
20
+ return humanizeLabel(field, definition.fields[field]);
21
+ }
22
+ /** Resolve a relationship target by `definitions` key, then by `name`. */
23
+ function targetOf(definitions, collection) {
24
+ return (definitions[collection] ??
25
+ Object.values(definitions).find((d) => d.name === collection));
26
+ }
27
+ /**
28
+ * Relations of `model` (a qualified `objectRef`, or a key of `definitions`).
29
+ * `items` come first, then `children`, each sorted by label; `reference`
30
+ * relations follow in declaration order. A child with two FKs to the same
31
+ * parent yields two relations, labelled `Orders (Owner)` /
32
+ * `Orders (Approver)` (and is only `items` when an edge says so).
33
+ */
34
+ export function deriveScreenRelations(definitions, model) {
35
+ const self = Object.values(definitions).find((d) => d.objectRef === model) ??
36
+ definitions[model];
37
+ if (!self)
38
+ return [];
39
+ const children = [];
40
+ const perChild = new Map();
41
+ for (const child of Object.values(definitions)) {
42
+ const edges = (child.relationships ?? []).filter((edge) => FK_KINDS.has(edge.kind) &&
43
+ edge.field in child.fields &&
44
+ targetOf(definitions, edge.relatedCollection)?.objectRef ===
45
+ self.objectRef);
46
+ const titles = screenTitles(child);
47
+ const roles = edges.length > 0 ? deriveFieldRoles(child) : {};
48
+ const lineLike = hasLineItemRoles(roles);
49
+ const list = edges.map((edge) => {
50
+ const label = edges.length > 1
51
+ ? `${titles.plural} (${fieldLabel(edge.field, child)})`
52
+ : titles.plural;
53
+ if (edge.role === 'items' || (lineLike && edges.length === 1)) {
54
+ return {
55
+ model: child.objectRef,
56
+ field: edge.field,
57
+ label,
58
+ kind: 'items',
59
+ columns: lineItemColumns(child, roles, { exclude: [edge.field] }),
60
+ sort: LINE_ORDER_FIELDS.find((name) => name in child.fields) ??
61
+ 'createdAt',
62
+ };
63
+ }
64
+ return {
65
+ model: child.objectRef,
66
+ field: edge.field,
67
+ label,
68
+ kind: 'children',
69
+ columns: relatedColumns(child, roles, { exclude: [edge.field] }),
70
+ };
71
+ });
72
+ if (list.length > 0)
73
+ perChild.set(child.objectRef, list);
74
+ }
75
+ for (const list of perChild.values())
76
+ children.push(...list);
77
+ children.sort((a, b) => Number(b.kind === 'items') - Number(a.kind === 'items') ||
78
+ a.label.localeCompare(b.label));
79
+ const references = [];
80
+ const seen = new Set();
81
+ for (const edge of self.relationships ?? []) {
82
+ if (!FK_KINDS.has(edge.kind) || seen.has(edge.field))
83
+ continue;
84
+ if (!(edge.field in self.fields))
85
+ continue;
86
+ const target = targetOf(definitions, edge.relatedCollection);
87
+ if (!target)
88
+ continue;
89
+ seen.add(edge.field);
90
+ references.push({
91
+ model: target.objectRef,
92
+ field: edge.field,
93
+ label: fieldLabel(edge.field, self),
94
+ kind: 'reference',
95
+ });
96
+ }
97
+ return [...children, ...references];
98
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Field roles (#3718 record page): what a field MEANS on a record page, as
3
+ * opposed to how it is stored. A record header reads its title, identifier,
4
+ * status and key facts by role; related tables pick their columns by role;
5
+ * line items and the totals block read quantity, money and the
6
+ * subtotal / tax / discount / total roles.
7
+ *
8
+ * Pure and Node-tested. Sources, in order:
9
+ *
10
+ * 1. Explicit metadata on the field definition: `role`, `format`, `enum`,
11
+ * `display`. An explicit `role` is authoritative: name heuristics never
12
+ * add to it (a `format` / `display` still does).
13
+ * 2. Manifest `ui` hints: `ui.format` (`money` / `date` / `datetime`), and
14
+ * `ui.widget: 'currency'` on an INTEGER field (the legacy money hint; on a
15
+ * text field it names the currency CODE, which is not money).
16
+ * 3. Name heuristics, only for fields with no explicit `role`.
17
+ *
18
+ * Money is integer minor units only (the cross-package invariant), so the
19
+ * name heuristics never mark a decimal as money; an explicit `role` or
20
+ * `format` on a decimal is honoured as declared.
21
+ */
22
+ import type { ScreenCollectionDefinition } from './types.js';
23
+ /** What a field means on a record page. One field may hold several roles. */
24
+ export type FieldRole = 'title' | 'identifier' | 'status' | 'money' | 'quantity' | 'date' | 'party' | 'subtotal' | 'tax' | 'discount' | 'total';
25
+ /** Every role, in the order a role list is reported. */
26
+ export declare const FIELD_ROLES: readonly FieldRole[];
27
+ /** Roles of every declared field of a definition, keyed by field name. */
28
+ export type ScreenFieldRoles = Readonly<Record<string, readonly FieldRole[]>>;
29
+ /**
30
+ * Roles of every declared field of `definition` (a key per field, possibly
31
+ * an empty list). Without an explicit `display` field, the first text field
32
+ * named `name` / `title` / `label` / `displayName` takes the `title` role.
33
+ */
34
+ export declare function deriveFieldRoles(definition: ScreenCollectionDefinition): Record<string, FieldRole[]>;
35
+ /** Fields holding `role`, in declaration order. */
36
+ export declare function fieldsWithRole(roles: ScreenFieldRoles, role: FieldRole): string[];
37
+ /** The first field holding `role`, or `undefined`. */
38
+ export declare function fieldWithRole(roles: ScreenFieldRoles, role: FieldRole): string | undefined;
39
+ /**
40
+ * The field naming a record's currency: a text field hinted
41
+ * `ui.widget: 'currency'` (the manifest's currency-code picker), else a text
42
+ * field named `currency` / `currencyCode`.
43
+ */
44
+ export declare function currencyCodeField(definition: ScreenCollectionDefinition): string | undefined;
45
+ /**
46
+ * Whether records of this definition read like documents (an order, an
47
+ * invoice): an identifier plus a party or a total. Their header leads with
48
+ * the identifier ("Order GRA-3159") rather than a name.
49
+ */
50
+ export declare function isDocumentLike(roles: ScreenFieldRoles): boolean;
51
+ /**
52
+ * Whether a child definition has line-item roles: a quantity and a money
53
+ * field (`deriveScreenRelations` classifies such a child relation `items`).
54
+ */
55
+ export declare function hasLineItemRoles(roles: ScreenFieldRoles): boolean;
56
+ //# sourceMappingURL=roles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"roles.d.ts","sourceRoot":"","sources":["../../../src/components/screens/roles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,KAAK,EACV,0BAA0B,EAG3B,MAAM,YAAY,CAAC;AAEpB,6EAA6E;AAC7E,MAAM,MAAM,SAAS,GACjB,OAAO,GACP,YAAY,GACZ,QAAQ,GACR,OAAO,GACP,UAAU,GACV,MAAM,GACN,OAAO,GACP,UAAU,GACV,KAAK,GACL,UAAU,GACV,OAAO,CAAC;AAEZ,wDAAwD;AACxD,eAAO,MAAM,WAAW,EAAE,SAAS,SAAS,EAY3C,CAAC;AAEF,0EAA0E;AAC1E,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,CAAC,CAAC,CAAC;AAsG9E;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,0BAA0B,GACrC,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,CA0C7B;AAED,mDAAmD;AACnD,wBAAgB,cAAc,CAC5B,KAAK,EAAE,gBAAgB,EACvB,IAAI,EAAE,SAAS,GACd,MAAM,EAAE,CAEV;AAED,sDAAsD;AACtD,wBAAgB,aAAa,CAC3B,KAAK,EAAE,gBAAgB,EACvB,IAAI,EAAE,SAAS,GACd,MAAM,GAAG,SAAS,CAEpB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,0BAA0B,GACrC,MAAM,GAAG,SAAS,CAUpB;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAM/D;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAKjE"}