@fieldia/viewer 0.19.1 → 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.
Files changed (158) hide show
  1. package/README.md +18 -0
  2. package/bundle/fieldia.ar.js +2 -2
  3. package/bundle/fieldia.ar.js.map +2 -2
  4. package/bundle/fieldia.choice.js +5 -0
  5. package/bundle/fieldia.choice.js.map +7 -0
  6. package/bundle/fieldia.de.js +2 -2
  7. package/bundle/fieldia.de.js.map +2 -2
  8. package/bundle/fieldia.filters.js +5 -0
  9. package/bundle/fieldia.filters.js.map +7 -0
  10. package/bundle/fieldia.fr.js +2 -2
  11. package/bundle/fieldia.fr.js.map +2 -2
  12. package/bundle/fieldia.head.js +5 -0
  13. package/bundle/fieldia.head.js.map +7 -0
  14. package/bundle/fieldia.js +44 -44
  15. package/bundle/fieldia.js.map +4 -4
  16. package/bundle/fieldia.path.js +5 -0
  17. package/bundle/fieldia.path.js.map +7 -0
  18. package/bundle/fieldia.text.js +24 -0
  19. package/bundle/fieldia.text.js.map +7 -0
  20. package/package.json +7 -3
  21. package/src/index.d.mts +2 -1
  22. package/src/index.d.ts +2 -1
  23. package/src/index.js +6 -1
  24. package/src/index.js.map +1 -1
  25. package/src/index.mjs +1 -0
  26. package/src/index.mjs.map +1 -1
  27. package/src/lib/button-parts.d.mts +30 -0
  28. package/src/lib/button-parts.d.ts +30 -0
  29. package/src/lib/button-parts.js +24 -0
  30. package/src/lib/button-parts.js.map +1 -0
  31. package/src/lib/button-parts.mjs +18 -0
  32. package/src/lib/button-parts.mjs.map +1 -0
  33. package/src/lib/dialog-window.d.mts +11 -0
  34. package/src/lib/dialog-window.d.ts +11 -0
  35. package/src/lib/dialog-window.js +86 -0
  36. package/src/lib/dialog-window.js.map +1 -0
  37. package/src/lib/dialog-window.mjs +82 -0
  38. package/src/lib/dialog-window.mjs.map +1 -0
  39. package/src/lib/dialog.d.mts +14 -3
  40. package/src/lib/dialog.d.ts +14 -3
  41. package/src/lib/dialog.js +84 -14
  42. package/src/lib/dialog.js.map +1 -1
  43. package/src/lib/dialog.mjs +85 -15
  44. package/src/lib/dialog.mjs.map +1 -1
  45. package/src/lib/filter-bar.d.mts +39 -0
  46. package/src/lib/filter-bar.d.ts +39 -0
  47. package/src/lib/filter-bar.js +290 -0
  48. package/src/lib/filter-bar.js.map +1 -0
  49. package/src/lib/filter-bar.mjs +287 -0
  50. package/src/lib/filter-bar.mjs.map +1 -0
  51. package/src/lib/filter-styles.d.mts +4 -0
  52. package/src/lib/filter-styles.d.ts +4 -0
  53. package/src/lib/filter-styles.js +63 -0
  54. package/src/lib/filter-styles.js.map +1 -0
  55. package/src/lib/filter-styles.mjs +59 -0
  56. package/src/lib/filter-styles.mjs.map +1 -0
  57. package/src/lib/filters-part.d.mts +8 -0
  58. package/src/lib/filters-part.d.ts +8 -0
  59. package/src/lib/filters-part.js +14 -0
  60. package/src/lib/filters-part.js.map +1 -0
  61. package/src/lib/filters-part.mjs +9 -0
  62. package/src/lib/filters-part.mjs.map +1 -0
  63. package/src/lib/group-styles.d.mts +9 -0
  64. package/src/lib/group-styles.d.ts +9 -0
  65. package/src/lib/group-styles.js +64 -0
  66. package/src/lib/group-styles.js.map +1 -0
  67. package/src/lib/group-styles.mjs +60 -0
  68. package/src/lib/group-styles.mjs.map +1 -0
  69. package/src/lib/head-part.d.mts +15 -0
  70. package/src/lib/head-part.d.ts +15 -0
  71. package/src/lib/head-part.js +26 -0
  72. package/src/lib/head-part.js.map +1 -0
  73. package/src/lib/head-part.mjs +16 -0
  74. package/src/lib/head-part.mjs.map +1 -0
  75. package/src/lib/head-styles.d.mts +28 -0
  76. package/src/lib/head-styles.d.ts +28 -0
  77. package/src/lib/head-styles.js +320 -0
  78. package/src/lib/head-styles.js.map +1 -0
  79. package/src/lib/head-styles.mjs +316 -0
  80. package/src/lib/head-styles.mjs.map +1 -0
  81. package/src/lib/labels.d.mts +29 -0
  82. package/src/lib/labels.d.ts +29 -0
  83. package/src/lib/labels.js +16 -0
  84. package/src/lib/labels.js.map +1 -1
  85. package/src/lib/labels.mjs +16 -0
  86. package/src/lib/labels.mjs.map +1 -1
  87. package/src/lib/list-styles.d.mts +1 -1
  88. package/src/lib/list-styles.d.ts +1 -1
  89. package/src/lib/list-styles.js +88 -1
  90. package/src/lib/list-styles.js.map +1 -1
  91. package/src/lib/list-styles.mjs +88 -1
  92. package/src/lib/list-styles.mjs.map +1 -1
  93. package/src/lib/list.d.mts +16 -1
  94. package/src/lib/list.d.ts +16 -1
  95. package/src/lib/list.js +281 -51
  96. package/src/lib/list.js.map +1 -1
  97. package/src/lib/list.mjs +282 -52
  98. package/src/lib/list.mjs.map +1 -1
  99. package/src/lib/locales/ar.js +16 -0
  100. package/src/lib/locales/ar.js.map +1 -1
  101. package/src/lib/locales/ar.mjs +16 -0
  102. package/src/lib/locales/ar.mjs.map +1 -1
  103. package/src/lib/locales/de.js +16 -0
  104. package/src/lib/locales/de.js.map +1 -1
  105. package/src/lib/locales/de.mjs +16 -0
  106. package/src/lib/locales/de.mjs.map +1 -1
  107. package/src/lib/locales/fr.js +16 -0
  108. package/src/lib/locales/fr.js.map +1 -1
  109. package/src/lib/locales/fr.mjs +16 -0
  110. package/src/lib/locales/fr.mjs.map +1 -1
  111. package/src/lib/look.d.mts +1 -1
  112. package/src/lib/look.d.ts +1 -1
  113. package/src/lib/look.js +3 -0
  114. package/src/lib/look.js.map +1 -1
  115. package/src/lib/look.mjs +3 -0
  116. package/src/lib/look.mjs.map +1 -1
  117. package/src/lib/record-bar.d.mts +10 -1
  118. package/src/lib/record-bar.d.ts +10 -1
  119. package/src/lib/record-bar.js +15 -7
  120. package/src/lib/record-bar.js.map +1 -1
  121. package/src/lib/record-bar.mjs +14 -7
  122. package/src/lib/record-bar.mjs.map +1 -1
  123. package/src/lib/record-head.d.mts +58 -0
  124. package/src/lib/record-head.d.ts +58 -0
  125. package/src/lib/record-head.js +245 -0
  126. package/src/lib/record-head.js.map +1 -0
  127. package/src/lib/record-head.mjs +240 -0
  128. package/src/lib/record-head.mjs.map +1 -0
  129. package/src/lib/rich-button.d.mts +12 -0
  130. package/src/lib/rich-button.d.ts +12 -0
  131. package/src/lib/rich-button.js +186 -0
  132. package/src/lib/rich-button.js.map +1 -0
  133. package/src/lib/rich-button.mjs +183 -0
  134. package/src/lib/rich-button.mjs.map +1 -0
  135. package/src/lib/section-head.d.mts +26 -0
  136. package/src/lib/section-head.d.ts +26 -0
  137. package/src/lib/section-head.js +65 -0
  138. package/src/lib/section-head.js.map +1 -0
  139. package/src/lib/section-head.mjs +56 -0
  140. package/src/lib/section-head.mjs.map +1 -0
  141. package/src/lib/section-tools.d.mts +12 -0
  142. package/src/lib/section-tools.d.ts +12 -0
  143. package/src/lib/section-tools.js +124 -0
  144. package/src/lib/section-tools.js.map +1 -0
  145. package/src/lib/section-tools.mjs +121 -0
  146. package/src/lib/section-tools.mjs.map +1 -0
  147. package/src/lib/viewer-parts.d.mts +33 -0
  148. package/src/lib/viewer-parts.d.ts +33 -0
  149. package/src/lib/viewer-parts.js +75 -0
  150. package/src/lib/viewer-parts.js.map +1 -0
  151. package/src/lib/viewer-parts.mjs +70 -0
  152. package/src/lib/viewer-parts.mjs.map +1 -0
  153. package/src/lib/viewer.d.mts +30 -7
  154. package/src/lib/viewer.d.ts +30 -7
  155. package/src/lib/viewer.js +317 -97
  156. package/src/lib/viewer.js.map +1 -1
  157. package/src/lib/viewer.mjs +319 -99
  158. package/src/lib/viewer.mjs.map +1 -1
@@ -0,0 +1,121 @@
1
+ import { drawIcon } from '@fieldia/widgets';
2
+ import { installGroupStyles } from './group-styles.mjs';
3
+ /**
4
+ * A group's heading as a work item draws it, with its tools at its end: ⤢ that
5
+ * shows the group alone over the record's body, ^ and ⌄ that fold it, and the
6
+ * tools of a field it holds alone. The head part's (head-part.ts).
7
+ */
8
+ const fill = (template, values) => template.replace(/\{(\w+)\}/g, (_, key) => values[key] ?? '');
9
+ /**
10
+ * The heading of a group with tools at its end. `show` folds and unfolds it;
11
+ * the section is marked folded (`fd-section-folded`) and maximised
12
+ * (`data-maximised`) for the stylesheet.
13
+ */
14
+ export function sectionHeading(kit, node, section, content, description, own) {
15
+ const { el, doc, labels } = kit;
16
+ installGroupStyles(doc);
17
+ const title = node.title ?? '';
18
+ const tools = el('span', { class: 'fd-section-tools' });
19
+ const legend = el('legend', { class: 'fd-section-title fd-section-head' }, el('span', { class: 'fd-section-name' }, ...kit.withIcon(node.icon, title)), tools);
20
+ section.setAttribute('data-head', '');
21
+ const icon = (name) => drawIcon(doc, name, kit.icons) ?? '';
22
+ const tool = (name, words) => {
23
+ const button = el('button', { type: 'button', class: 'fd-section-tool', 'data-tool': name, 'aria-label': words, title: words });
24
+ return button;
25
+ };
26
+ // ⤢: the group alone over the record's body, until pressed again or Escape.
27
+ let undo = null;
28
+ const big = node.maximise ? tool('maximise', fill(labels.maximiseNamed, { name: title })) : null;
29
+ const restore = () => {
30
+ undo?.();
31
+ undo = null;
32
+ if (!big)
33
+ return;
34
+ big.setAttribute('aria-pressed', 'false');
35
+ big.replaceChildren(icon('maximise'));
36
+ };
37
+ if (big) {
38
+ big.setAttribute('aria-pressed', 'false');
39
+ big.append(icon('maximise'));
40
+ big.addEventListener('click', () => {
41
+ if (undo)
42
+ return restore();
43
+ undo = maximise(section);
44
+ big.setAttribute('aria-pressed', 'true');
45
+ big.replaceChildren(icon('restore'));
46
+ big.focus();
47
+ });
48
+ section.addEventListener('keydown', (event) => {
49
+ if (event.key !== 'Escape' || !undo || event.defaultPrevented)
50
+ return;
51
+ event.preventDefault();
52
+ event.stopPropagation();
53
+ restore();
54
+ big.focus();
55
+ });
56
+ tools.append(big);
57
+ }
58
+ // ^ and ⌄: the group folded and unfolded from its heading's end.
59
+ let open = node.collapsed !== true;
60
+ const fold = node.collapsible ? tool('fold', '') : null;
61
+ const show = (next) => {
62
+ open = next;
63
+ if (fold) {
64
+ const words = fill(open ? labels.foldNamed : labels.unfoldNamed, { name: title });
65
+ fold.setAttribute('aria-label', words);
66
+ fold.title = words;
67
+ fold.setAttribute('aria-expanded', String(open));
68
+ fold.replaceChildren(icon(open ? 'chevron-up' : 'chevron-down'));
69
+ }
70
+ content.hidden = !open;
71
+ if (description)
72
+ description.hidden = !open;
73
+ section.classList.toggle('fd-section-folded', !open);
74
+ if (!open && undo)
75
+ restore();
76
+ };
77
+ if (fold) {
78
+ fold.setAttribute('aria-controls', content.id);
79
+ fold.addEventListener('click', () => show(!open));
80
+ tools.append(fold);
81
+ }
82
+ // The tools of the field it holds alone, last.
83
+ tools.append(...own);
84
+ show(open);
85
+ return { legend, show, isOpen: () => open };
86
+ }
87
+ /**
88
+ * The group alone over the record's body: every part beside it, and beside
89
+ * each part round it up to the body, out of sight; the grids round it give it
90
+ * their whole width. Returns how to put it all back.
91
+ */
92
+ function maximise(section) {
93
+ const host = section.closest('.fd-tabpanel') ?? section.closest('.fd-content') ?? section.closest('.fd-form');
94
+ const unshown = [];
95
+ const grids = [];
96
+ let at = section;
97
+ while (at !== host && at.parentElement) {
98
+ const parent = at.parentElement;
99
+ for (const sibling of [...parent.children]) {
100
+ if (sibling === at || sibling.tagName === 'LEGEND' || sibling.hasAttribute('data-unshown'))
101
+ continue;
102
+ sibling.setAttribute('data-unshown', '');
103
+ unshown.push(sibling);
104
+ }
105
+ if (parent.classList.contains('fd-grid')) {
106
+ parent.setAttribute('data-maximising', '');
107
+ grids.push(parent);
108
+ }
109
+ at = parent;
110
+ }
111
+ section.setAttribute('data-maximised', '');
112
+ section.scrollIntoView?.({ block: 'nearest' });
113
+ return () => {
114
+ for (const part of unshown)
115
+ part.removeAttribute('data-unshown');
116
+ for (const grid of grids)
117
+ grid.removeAttribute('data-maximising');
118
+ section.removeAttribute('data-maximised');
119
+ };
120
+ }
121
+ //# sourceMappingURL=section-tools.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"section-tools.mjs","sourceRoot":"","sources":["../../../../../libs/viewer/src/lib/section-tools.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import type { Page } from '@fieldia/core';
2
+ import { type Part, type WidgetFactory } from '@fieldia/widgets';
3
+ import type * as FiltersModule from './filters-part.mjs';
4
+ import type * as HeadModule from './head-part.mjs';
5
+ /** The head part: a record's head, split and toggled buttons, a group's work heading, the inset dialog's window tools. */
6
+ export type HeadPart = typeof HeadModule;
7
+ /** The filters part: a list's filter bar. */
8
+ export type FiltersPart = typeof FiltersModule;
9
+ /** A work item's record and its window, loaded the first time a page draws a head, a split button, a work heading or the inset dialog. */
10
+ export declare const headPart: Part<HeadPart>;
11
+ /** A list's filter bar, loaded the first time a list draws one. */
12
+ export declare const filtersPart: Part<FiltersPart>;
13
+ /**
14
+ * The parts a page draws from (parts.ts in @fieldia/widgets): the head part for
15
+ * a record's head, a button split, toggled or shown by its icon alone, or a
16
+ * group's work heading; the filters part for a list's filter bar; and the
17
+ * widgets' own for the fields that need them (a work item's text, a links box,
18
+ * a path picker, a choice in Fieldia's own list) — unless the app's `widgets` draw those fields; and those the
19
+ * app's `slots` name for the slots the page has (a Discussion's).
20
+ */
21
+ export declare function partsFor(page: Page, widgets?: Record<string, WidgetFactory>, slots?: Record<string, {
22
+ readonly parts?: readonly Part[];
23
+ }>): Part[];
24
+ /**
25
+ * The parts a page draws from, loaded — or, with no page, every part. The
26
+ * viewer and its dialogs wait for a page's parts themselves before they draw
27
+ * it; this is for an app that wants them in before it mounts, so the page is
28
+ * drawn whole the moment it is mounted. Rejects when one could not come (the
29
+ * viewer then draws plainly what it would have drawn from it).
30
+ */
31
+ export declare function loadParts(page?: Page, widgets?: Record<string, WidgetFactory>, slots?: Record<string, {
32
+ readonly parts?: readonly Part[];
33
+ }>): Promise<void>;
@@ -0,0 +1,33 @@
1
+ import type { Page } from '@fieldia/core';
2
+ import { type Part, type WidgetFactory } from '@fieldia/widgets';
3
+ import type * as FiltersModule from './filters-part';
4
+ import type * as HeadModule from './head-part';
5
+ /** The head part: a record's head, split and toggled buttons, a group's work heading, the inset dialog's window tools. */
6
+ export type HeadPart = typeof HeadModule;
7
+ /** The filters part: a list's filter bar. */
8
+ export type FiltersPart = typeof FiltersModule;
9
+ /** A work item's record and its window, loaded the first time a page draws a head, a split button, a work heading or the inset dialog. */
10
+ export declare const headPart: Part<HeadPart>;
11
+ /** A list's filter bar, loaded the first time a list draws one. */
12
+ export declare const filtersPart: Part<FiltersPart>;
13
+ /**
14
+ * The parts a page draws from (parts.ts in @fieldia/widgets): the head part for
15
+ * a record's head, a button split, toggled or shown by its icon alone, or a
16
+ * group's work heading; the filters part for a list's filter bar; and the
17
+ * widgets' own for the fields that need them (a work item's text, a links box,
18
+ * a path picker, a choice in Fieldia's own list) — unless the app's `widgets` draw those fields; and those the
19
+ * app's `slots` name for the slots the page has (a Discussion's).
20
+ */
21
+ export declare function partsFor(page: Page, widgets?: Record<string, WidgetFactory>, slots?: Record<string, {
22
+ readonly parts?: readonly Part[];
23
+ }>): Part[];
24
+ /**
25
+ * The parts a page draws from, loaded — or, with no page, every part. The
26
+ * viewer and its dialogs wait for a page's parts themselves before they draw
27
+ * it; this is for an app that wants them in before it mounts, so the page is
28
+ * drawn whole the moment it is mounted. Rejects when one could not come (the
29
+ * viewer then draws plainly what it would have drawn from it).
30
+ */
31
+ export declare function loadParts(page?: Page, widgets?: Record<string, WidgetFactory>, slots?: Record<string, {
32
+ readonly parts?: readonly Part[];
33
+ }>): Promise<void>;
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.filtersPart = exports.headPart = void 0;
4
+ exports.partsFor = partsFor;
5
+ exports.loadParts = loadParts;
6
+ const widgets_1 = require("@fieldia/widgets");
7
+ const button_parts_1 = require("./button-parts");
8
+ const section_head_1 = require("./section-head");
9
+ /** A work item's record and its window, loaded the first time a page draws a head, a split button, a work heading or the inset dialog. */
10
+ exports.headPart = (0, widgets_1.lazyPart)('head', () => Promise.resolve().then(() => require('./head-part')));
11
+ /** A list's filter bar, loaded the first time a list draws one. */
12
+ exports.filtersPart = (0, widgets_1.lazyPart)('filters', () => Promise.resolve().then(() => require('./filters-part')));
13
+ /** Every part the viewer can draw from, its own and its widgets'. */
14
+ const EVERY_PART = [exports.headPart, exports.filtersPart, widgets_1.textPart, widgets_1.pathPart, widgets_1.choicePart];
15
+ /** A group holding a Markdown text alone, whose Edit and Done its heading takes: drawn with the work heading's tools. */
16
+ const markdownAlone = (node) => {
17
+ const only = node.children.length === 1 ? node.children[0] : undefined;
18
+ return !!node.title && only?.type === 'field' && only.options?.['editor'] === 'markdown';
19
+ };
20
+ /**
21
+ * The parts a page draws from (parts.ts in @fieldia/widgets): the head part for
22
+ * a record's head, a button split, toggled or shown by its icon alone, or a
23
+ * group's work heading; the filters part for a list's filter bar; and the
24
+ * widgets' own for the fields that need them (a work item's text, a links box,
25
+ * a path picker, a choice in Fieldia's own list) — unless the app's `widgets` draw those fields; and those the
26
+ * app's `slots` name for the slots the page has (a Discussion's).
27
+ */
28
+ function partsFor(page, widgets = {}, slots = {}) {
29
+ const found = new Set();
30
+ const visit = (value) => {
31
+ if (Array.isArray(value)) {
32
+ for (const item of value)
33
+ visit(item);
34
+ return;
35
+ }
36
+ if (!value || typeof value !== 'object')
37
+ return;
38
+ const node = value;
39
+ if (node.type === 'sheet' && node['head'])
40
+ found.add(exports.headPart);
41
+ else if (node.type === 'button' && (0, button_parts_1.isRichButton)(node))
42
+ found.add(exports.headPart);
43
+ else if (node.type === 'section' && Array.isArray(node['children']) && ((0, section_head_1.workHeading)(node) || markdownAlone(node)))
44
+ found.add(exports.headPart);
45
+ else if (node.type === 'list' && node['search'] === 'fields')
46
+ found.add(exports.filtersPart);
47
+ else if (node.type === 'slot' && typeof node['name'] === 'string')
48
+ for (const part of slots[node['name']]?.parts ?? [])
49
+ found.add(part);
50
+ else if (node.type === 'field' && typeof node['field'] === 'string') {
51
+ const field = page.fields[node['field']];
52
+ const part = field ? (0, widgets_1.widgetPart)(field, node, widgets) : undefined;
53
+ if (part)
54
+ found.add(part);
55
+ }
56
+ for (const key of Object.keys(node)) {
57
+ const child = node[key];
58
+ if (child && typeof child === 'object')
59
+ visit(child);
60
+ }
61
+ };
62
+ visit(page.layout);
63
+ return [...found];
64
+ }
65
+ /**
66
+ * The parts a page draws from, loaded — or, with no page, every part. The
67
+ * viewer and its dialogs wait for a page's parts themselves before they draw
68
+ * it; this is for an app that wants them in before it mounts, so the page is
69
+ * drawn whole the moment it is mounted. Rejects when one could not come (the
70
+ * viewer then draws plainly what it would have drawn from it).
71
+ */
72
+ async function loadParts(page, widgets, slots) {
73
+ await Promise.all((page ? partsFor(page, widgets, slots) : EVERY_PART).map((part) => part.load()));
74
+ }
75
+ //# sourceMappingURL=viewer-parts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viewer-parts.js","sourceRoot":"","sources":["../../../../../libs/viewer/src/lib/viewer-parts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ import { choicePart, lazyPart, pathPart, textPart, widgetPart } from '@fieldia/widgets';
2
+ import { isRichButton } from './button-parts.mjs';
3
+ import { workHeading } from './section-head.mjs';
4
+ /** A work item's record and its window, loaded the first time a page draws a head, a split button, a work heading or the inset dialog. */
5
+ export const headPart = lazyPart('head', () => import('./head-part.mjs'));
6
+ /** A list's filter bar, loaded the first time a list draws one. */
7
+ export const filtersPart = lazyPart('filters', () => import('./filters-part.mjs'));
8
+ /** Every part the viewer can draw from, its own and its widgets'. */
9
+ const EVERY_PART = [headPart, filtersPart, textPart, pathPart, choicePart];
10
+ /** A group holding a Markdown text alone, whose Edit and Done its heading takes: drawn with the work heading's tools. */
11
+ const markdownAlone = (node) => {
12
+ const only = node.children.length === 1 ? node.children[0] : undefined;
13
+ return !!node.title && only?.type === 'field' && only.options?.['editor'] === 'markdown';
14
+ };
15
+ /**
16
+ * The parts a page draws from (parts.ts in @fieldia/widgets): the head part for
17
+ * a record's head, a button split, toggled or shown by its icon alone, or a
18
+ * group's work heading; the filters part for a list's filter bar; and the
19
+ * widgets' own for the fields that need them (a work item's text, a links box,
20
+ * a path picker, a choice in Fieldia's own list) — unless the app's `widgets` draw those fields; and those the
21
+ * app's `slots` name for the slots the page has (a Discussion's).
22
+ */
23
+ export function partsFor(page, widgets = {}, slots = {}) {
24
+ const found = new Set();
25
+ const visit = (value) => {
26
+ if (Array.isArray(value)) {
27
+ for (const item of value)
28
+ visit(item);
29
+ return;
30
+ }
31
+ if (!value || typeof value !== 'object')
32
+ return;
33
+ const node = value;
34
+ if (node.type === 'sheet' && node['head'])
35
+ found.add(headPart);
36
+ else if (node.type === 'button' && isRichButton(node))
37
+ found.add(headPart);
38
+ else if (node.type === 'section' && Array.isArray(node['children']) && (workHeading(node) || markdownAlone(node)))
39
+ found.add(headPart);
40
+ else if (node.type === 'list' && node['search'] === 'fields')
41
+ found.add(filtersPart);
42
+ else if (node.type === 'slot' && typeof node['name'] === 'string')
43
+ for (const part of slots[node['name']]?.parts ?? [])
44
+ found.add(part);
45
+ else if (node.type === 'field' && typeof node['field'] === 'string') {
46
+ const field = page.fields[node['field']];
47
+ const part = field ? widgetPart(field, node, widgets) : undefined;
48
+ if (part)
49
+ found.add(part);
50
+ }
51
+ for (const key of Object.keys(node)) {
52
+ const child = node[key];
53
+ if (child && typeof child === 'object')
54
+ visit(child);
55
+ }
56
+ };
57
+ visit(page.layout);
58
+ return [...found];
59
+ }
60
+ /**
61
+ * The parts a page draws from, loaded — or, with no page, every part. The
62
+ * viewer and its dialogs wait for a page's parts themselves before they draw
63
+ * it; this is for an app that wants them in before it mounts, so the page is
64
+ * drawn whole the moment it is mounted. Rejects when one could not come (the
65
+ * viewer then draws plainly what it would have drawn from it).
66
+ */
67
+ export async function loadParts(page, widgets, slots) {
68
+ await Promise.all((page ? partsFor(page, widgets, slots) : EVERY_PART).map((part) => part.load()));
69
+ }
70
+ //# sourceMappingURL=viewer-parts.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viewer-parts.mjs","sourceRoot":"","sources":["../../../../../libs/viewer/src/lib/viewer-parts.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { type ActionHost, type ActionStep, type FormEvents, type Form, type FormOptions, type LayoutNode, type Page, type Skin, type Theme, type RecordId, type ResolvedFilter, type Locale, type OpenRequest, type OpenResult, type RunContext, type RunResult, type Values } from '@fieldia/core';
2
- import { type IconSet, type PreferenceStore, type WidgetFactory } from '@fieldia/widgets';
2
+ import { type IconSet, type Part, type PreferenceStore, type WidgetFactory } from '@fieldia/widgets';
3
3
  import { type PageFinder } from './related.mjs';
4
- import { type ListViewFactory } from './list.mjs';
4
+ import { type ListViewFactory, type OpenRowOptions } from './list.mjs';
5
5
  import type { MapPartFactory } from './map-part.mjs';
6
6
  import { type Tokens } from './look.mjs';
7
7
  import { type Breadcrumb, type RecordPager } from './record-bar.mjs';
@@ -11,11 +11,16 @@ export type { Skin };
11
11
  import { type ViewerLabels } from './labels.mjs';
12
12
  export { VIEWER_LABELS, DEFAULT_LABELS, type ViewerLabels } from './labels.mjs';
13
13
  export type { PageFinder, PageRequest } from './related.mjs';
14
- /** Fill a slot with the app's own content. Return a function to clean up. */
15
- export type SlotRenderer = (element: HTMLElement, context: {
14
+ /**
15
+ * Fill a slot with the app's own content. Return a function to clean up. A slot drawn from parts loaded on first use
16
+ * (a work item's Discussion: parts.ts in @fieldia/widgets) names them in `parts`: the page is drawn once they are in.
17
+ */
18
+ export type SlotRenderer = ((element: HTMLElement, context: {
16
19
  form: Form;
17
20
  name: string;
18
- }) => void | (() => void);
21
+ }) => void | (() => void)) & {
22
+ readonly parts?: readonly Part[];
23
+ };
19
24
  export interface ViewerOptions extends Omit<FormOptions, 'page' | 'host'> {
20
25
  page: Page;
21
26
  /** A form made elsewhere, to share one record between two views. */
@@ -101,8 +106,13 @@ export interface ViewerOptions extends Omit<FormOptions, 'page' | 'host'> {
101
106
  * key: every word of the page goes through it. Record data never does.
102
107
  */
103
108
  translate?: (text: string) => string;
104
- /** A list page: a row was opened, by a click or by Enter. The app shows the record. */
105
- onOpenRecord?: (id: RecordId) => void;
109
+ /**
110
+ * A list page: a row was opened, by a click or by Enter. The app shows the
111
+ * record. When it answers with a promise — the record in a dialog, closed —
112
+ * the list asks for its records again once it settles, and puts the focus
113
+ * back on the row. Anything else it answers is let be.
114
+ */
115
+ onOpenRecord?: (id: RecordId) => unknown;
106
116
  /** A list page: only the records these conditions let through, whatever is searched — the invoices of one order, opened from it. */
107
117
  listFilter?: ResolvedFilter[];
108
118
  /**
@@ -150,12 +160,25 @@ export interface ViewerHandle {
150
160
  /** Lock or unlock the whole form. */
151
161
  setReadonly(readonly: boolean): void;
152
162
  isReadonly(): boolean;
163
+ /** Ask the data source again: a list page's records, keeping its page, order and search; else the record, as a reload step does. */
164
+ reload(): Promise<void>;
165
+ /**
166
+ * A list page: open a record as its row's press does — the app's onOpenRecord, the row marked open (aria-current) while
167
+ * it is, then the records again and the focus back on its row — as when the app opens one from its address; there
168
+ * `{ lit: false }` leaves its row unmarked, as no row was pressed. A record page has no rows: nothing happens.
169
+ */
170
+ openRecord(id: RecordId, options?: OpenRowOptions): Promise<void>;
153
171
  /** Listen to one of the form's events — change, save, send, action, run, step, open; returns what stops listening. */
154
172
  on<E extends keyof FormEvents>(event: E, listener: (event: FormEvents[E]) => void): () => void;
155
173
  /** Write several fields at once, as the app: the page's change steps do not run for it. */
156
174
  setValues(values: Values): void;
157
175
  /** Run steps on the form, as a button would. */
158
176
  run(steps: readonly ActionStep[], context?: RunContext): Promise<RunResult>;
177
+ /**
178
+ * Settled once the page is drawn: at once, unless it draws from a part not loaded yet (a record's head, a filter bar, a
179
+ * work item's text, a path picker: parts.ts), which it waits for — its element empty and busy meanwhile.
180
+ */
181
+ readonly drawn: Promise<void>;
159
182
  destroy(): void;
160
183
  }
161
184
  /**
@@ -1,7 +1,7 @@
1
1
  import { type ActionHost, type ActionStep, type FormEvents, type Form, type FormOptions, type LayoutNode, type Page, type Skin, type Theme, type RecordId, type ResolvedFilter, type Locale, type OpenRequest, type OpenResult, type RunContext, type RunResult, type Values } from '@fieldia/core';
2
- import { type IconSet, type PreferenceStore, type WidgetFactory } from '@fieldia/widgets';
2
+ import { type IconSet, type Part, type PreferenceStore, type WidgetFactory } from '@fieldia/widgets';
3
3
  import { type PageFinder } from './related';
4
- import { type ListViewFactory } from './list';
4
+ import { type ListViewFactory, type OpenRowOptions } from './list';
5
5
  import type { MapPartFactory } from './map-part';
6
6
  import { type Tokens } from './look';
7
7
  import { type Breadcrumb, type RecordPager } from './record-bar';
@@ -11,11 +11,16 @@ export type { Skin };
11
11
  import { type ViewerLabels } from './labels';
12
12
  export { VIEWER_LABELS, DEFAULT_LABELS, type ViewerLabels } from './labels';
13
13
  export type { PageFinder, PageRequest } from './related';
14
- /** Fill a slot with the app's own content. Return a function to clean up. */
15
- export type SlotRenderer = (element: HTMLElement, context: {
14
+ /**
15
+ * Fill a slot with the app's own content. Return a function to clean up. A slot drawn from parts loaded on first use
16
+ * (a work item's Discussion: parts.ts in @fieldia/widgets) names them in `parts`: the page is drawn once they are in.
17
+ */
18
+ export type SlotRenderer = ((element: HTMLElement, context: {
16
19
  form: Form;
17
20
  name: string;
18
- }) => void | (() => void);
21
+ }) => void | (() => void)) & {
22
+ readonly parts?: readonly Part[];
23
+ };
19
24
  export interface ViewerOptions extends Omit<FormOptions, 'page' | 'host'> {
20
25
  page: Page;
21
26
  /** A form made elsewhere, to share one record between two views. */
@@ -101,8 +106,13 @@ export interface ViewerOptions extends Omit<FormOptions, 'page' | 'host'> {
101
106
  * key: every word of the page goes through it. Record data never does.
102
107
  */
103
108
  translate?: (text: string) => string;
104
- /** A list page: a row was opened, by a click or by Enter. The app shows the record. */
105
- onOpenRecord?: (id: RecordId) => void;
109
+ /**
110
+ * A list page: a row was opened, by a click or by Enter. The app shows the
111
+ * record. When it answers with a promise — the record in a dialog, closed —
112
+ * the list asks for its records again once it settles, and puts the focus
113
+ * back on the row. Anything else it answers is let be.
114
+ */
115
+ onOpenRecord?: (id: RecordId) => unknown;
106
116
  /** A list page: only the records these conditions let through, whatever is searched — the invoices of one order, opened from it. */
107
117
  listFilter?: ResolvedFilter[];
108
118
  /**
@@ -150,12 +160,25 @@ export interface ViewerHandle {
150
160
  /** Lock or unlock the whole form. */
151
161
  setReadonly(readonly: boolean): void;
152
162
  isReadonly(): boolean;
163
+ /** Ask the data source again: a list page's records, keeping its page, order and search; else the record, as a reload step does. */
164
+ reload(): Promise<void>;
165
+ /**
166
+ * A list page: open a record as its row's press does — the app's onOpenRecord, the row marked open (aria-current) while
167
+ * it is, then the records again and the focus back on its row — as when the app opens one from its address; there
168
+ * `{ lit: false }` leaves its row unmarked, as no row was pressed. A record page has no rows: nothing happens.
169
+ */
170
+ openRecord(id: RecordId, options?: OpenRowOptions): Promise<void>;
153
171
  /** Listen to one of the form's events — change, save, send, action, run, step, open; returns what stops listening. */
154
172
  on<E extends keyof FormEvents>(event: E, listener: (event: FormEvents[E]) => void): () => void;
155
173
  /** Write several fields at once, as the app: the page's change steps do not run for it. */
156
174
  setValues(values: Values): void;
157
175
  /** Run steps on the form, as a button would. */
158
176
  run(steps: readonly ActionStep[], context?: RunContext): Promise<RunResult>;
177
+ /**
178
+ * Settled once the page is drawn: at once, unless it draws from a part not loaded yet (a record's head, a filter bar, a
179
+ * work item's text, a path picker: parts.ts), which it waits for — its element empty and busy meanwhile.
180
+ */
181
+ readonly drawn: Promise<void>;
159
182
  destroy(): void;
160
183
  }
161
184
  /**