@fieldia/viewer 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +126 -1
- package/bundle/fieldia.ar.js +2 -2
- package/bundle/fieldia.ar.js.map +2 -2
- package/bundle/fieldia.de.js +2 -2
- package/bundle/fieldia.de.js.map +2 -2
- package/bundle/fieldia.fr.js +2 -2
- package/bundle/fieldia.fr.js.map +2 -2
- package/bundle/fieldia.js +6 -4
- package/bundle/fieldia.js.map +4 -4
- package/package.json +3 -3
- package/src/lib/attachment-preview.d.mts +27 -0
- package/src/lib/attachment-preview.d.ts +27 -0
- package/src/lib/attachment-preview.js +89 -0
- package/src/lib/attachment-preview.js.map +1 -0
- package/src/lib/attachment-preview.mjs +86 -0
- package/src/lib/attachment-preview.mjs.map +1 -0
- package/src/lib/dialog.d.mts +31 -2
- package/src/lib/dialog.d.ts +31 -2
- package/src/lib/dialog.js +158 -30
- package/src/lib/dialog.js.map +1 -1
- package/src/lib/dialog.mjs +155 -30
- package/src/lib/dialog.mjs.map +1 -1
- package/src/lib/hotkeys.d.mts +11 -0
- package/src/lib/hotkeys.d.ts +11 -0
- package/src/lib/hotkeys.js +34 -0
- package/src/lib/hotkeys.js.map +1 -0
- package/src/lib/hotkeys.mjs +30 -0
- package/src/lib/hotkeys.mjs.map +1 -0
- package/src/lib/labels.d.mts +39 -0
- package/src/lib/labels.d.ts +39 -0
- package/src/lib/labels.js +23 -0
- package/src/lib/labels.js.map +1 -1
- package/src/lib/labels.mjs +23 -0
- package/src/lib/labels.mjs.map +1 -1
- package/src/lib/list.d.mts +5 -2
- package/src/lib/list.d.ts +5 -2
- package/src/lib/list.js +6 -6
- package/src/lib/list.js.map +1 -1
- package/src/lib/list.mjs +6 -6
- package/src/lib/list.mjs.map +1 -1
- package/src/lib/locales/ar.js +23 -0
- package/src/lib/locales/ar.js.map +1 -1
- package/src/lib/locales/ar.mjs +23 -0
- package/src/lib/locales/ar.mjs.map +1 -1
- package/src/lib/locales/de.js +23 -0
- package/src/lib/locales/de.js.map +1 -1
- package/src/lib/locales/de.mjs +23 -0
- package/src/lib/locales/de.mjs.map +1 -1
- package/src/lib/locales/fr.js +23 -0
- package/src/lib/locales/fr.js.map +1 -1
- package/src/lib/locales/fr.mjs +23 -0
- package/src/lib/locales/fr.mjs.map +1 -1
- package/src/lib/look.d.mts +7 -3
- package/src/lib/look.d.ts +7 -3
- package/src/lib/look.js +72 -15
- package/src/lib/look.js.map +1 -1
- package/src/lib/look.mjs +72 -15
- package/src/lib/look.mjs.map +1 -1
- package/src/lib/open.d.mts +30 -0
- package/src/lib/open.d.ts +30 -0
- package/src/lib/open.js +92 -0
- package/src/lib/open.js.map +1 -0
- package/src/lib/open.mjs +89 -0
- package/src/lib/open.mjs.map +1 -0
- package/src/lib/place.d.mts +2 -0
- package/src/lib/place.d.ts +2 -0
- package/src/lib/place.js.map +1 -1
- package/src/lib/place.mjs.map +1 -1
- package/src/lib/record-bar.d.mts +56 -0
- package/src/lib/record-bar.d.ts +56 -0
- package/src/lib/record-bar.js +237 -0
- package/src/lib/record-bar.js.map +1 -0
- package/src/lib/record-bar.mjs +234 -0
- package/src/lib/record-bar.mjs.map +1 -0
- package/src/lib/record-styles.d.mts +9 -0
- package/src/lib/record-styles.d.ts +9 -0
- package/src/lib/record-styles.js +83 -0
- package/src/lib/record-styles.js.map +1 -0
- package/src/lib/record-styles.mjs +79 -0
- package/src/lib/record-styles.mjs.map +1 -0
- package/src/lib/related.d.mts +26 -3
- package/src/lib/related.d.ts +26 -3
- package/src/lib/related.js +37 -10
- package/src/lib/related.js.map +1 -1
- package/src/lib/related.mjs +36 -10
- package/src/lib/related.mjs.map +1 -1
- package/src/lib/say.d.mts +11 -0
- package/src/lib/say.d.ts +11 -0
- package/src/lib/say.js +61 -0
- package/src/lib/say.js.map +1 -0
- package/src/lib/say.mjs +58 -0
- package/src/lib/say.mjs.map +1 -0
- package/src/lib/tab-strip.d.mts +17 -0
- package/src/lib/tab-strip.d.ts +17 -0
- package/src/lib/tab-strip.js +51 -0
- package/src/lib/tab-strip.js.map +1 -0
- package/src/lib/tab-strip.mjs +48 -0
- package/src/lib/tab-strip.mjs.map +1 -0
- package/src/lib/value-words.d.mts +10 -0
- package/src/lib/value-words.d.ts +10 -0
- package/src/lib/value-words.js +38 -0
- package/src/lib/value-words.js.map +1 -0
- package/src/lib/value-words.mjs +35 -0
- package/src/lib/value-words.mjs.map +1 -0
- package/src/lib/viewer.d.mts +54 -5
- package/src/lib/viewer.d.ts +54 -5
- package/src/lib/viewer.js +924 -297
- package/src/lib/viewer.js.map +1 -1
- package/src/lib/viewer.mjs +926 -299
- package/src/lib/viewer.mjs.map +1 -1
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RECORD_STYLES = void 0;
|
|
4
|
+
exports.installRecordStyles = installRecordStyles;
|
|
5
|
+
/**
|
|
6
|
+
* What sits around a record, on the same tokens as the form: the bar over it
|
|
7
|
+
* (breadcrumbs, the gear menu, the pager), the attachment preview beside its
|
|
8
|
+
* sheet, and where the side panel goes. Put in the document after Fieldia's
|
|
9
|
+
* own stylesheet, so its rules on the sheet's layout come after those.
|
|
10
|
+
*/
|
|
11
|
+
exports.RECORD_STYLES = `
|
|
12
|
+
.fd-record-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; min-height: 46px; box-sizing: border-box; padding: 6px 16px; background: var(--fd-surface); border-block-end: 1px solid var(--fd-border); }
|
|
13
|
+
.fd-form > .fd-content > .fd-record-bar { margin: -4px 0 12px; border-radius: var(--fd-radius); border: 1px solid var(--fd-border); }
|
|
14
|
+
.fd-breadcrumbs { min-width: 0; }
|
|
15
|
+
.fd-breadcrumbs ol { display: flex; flex-wrap: wrap; align-items: center; list-style: none; margin: 0; padding: 0; font-size: 17px; line-height: 1.3; }
|
|
16
|
+
.fd-breadcrumbs li { display: flex; align-items: center; min-width: 0; }
|
|
17
|
+
.fd-breadcrumbs li + li::before { content: "/"; color: var(--fd-muted); padding-inline: 8px; }
|
|
18
|
+
.fd-crumb { all: unset; cursor: pointer; color: var(--fd-accent); border-radius: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
19
|
+
.fd-crumb:hover { text-decoration: underline; }
|
|
20
|
+
.fd-crumb:focus-visible { outline: 2px solid var(--fd-focus); outline-offset: 2px; }
|
|
21
|
+
.fd-crumb-current { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
22
|
+
.fd-record-actions { position: relative; }
|
|
23
|
+
.fd-record-actions[hidden] { display: none; }
|
|
24
|
+
.fd-record-gear { display: inline-flex; align-items: center; gap: 6px; }
|
|
25
|
+
.fd-record-gear svg { width: 16px; height: 16px; flex: none; }
|
|
26
|
+
.fd-record-menu { position: absolute; z-index: 40; inset-block-start: calc(100% + 4px); inset-inline-start: 0; display: grid; min-width: 220px; max-width: min(320px, 90vw); padding: 4px; box-sizing: border-box; background: var(--fd-surface); color: var(--fd-text); border: 1px solid var(--fd-border); border-radius: max(var(--fd-control-radius), 4px); box-shadow: 0 8px 24px rgba(0, 0, 0, 0.16); }
|
|
27
|
+
.fd-record-menu[data-end] { inset-inline-start: auto; inset-inline-end: 0; }
|
|
28
|
+
.fd-record-menu[hidden], .fd-record-menu [hidden] { display: none; }
|
|
29
|
+
.fd-record-menu [role="group"] { display: grid; }
|
|
30
|
+
.fd-record-menu-heading { padding: 6px 12px 2px; font-size: 12px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var(--fd-muted); }
|
|
31
|
+
.fd-record-menu-rule { margin: 4px 0; border-block-start: 1px solid var(--fd-border); }
|
|
32
|
+
.fd-record-menu-item { all: unset; box-sizing: border-box; display: flex; align-items: center; gap: 8px; min-height: 34px; padding: 6px 12px; border-radius: 3px; cursor: pointer; color: var(--fd-text); }
|
|
33
|
+
.fd-record-menu-item svg { width: 16px; height: 16px; flex: none; color: var(--fd-muted); }
|
|
34
|
+
.fd-record-menu-item:hover, .fd-record-menu-item:focus { background: var(--fd-accent-soft); }
|
|
35
|
+
.fd-record-menu-item:focus-visible { outline: 2px solid var(--fd-focus); outline-offset: -2px; }
|
|
36
|
+
.fd-record-menu-danger { color: var(--fd-error); }
|
|
37
|
+
.fd-record-pager { display: flex; align-items: center; gap: 4px; margin-inline-start: auto; }
|
|
38
|
+
.fd-record-pager[hidden] { display: none; }
|
|
39
|
+
.fd-record-pager-text { padding-inline-end: 6px; font-variant-numeric: tabular-nums; color: var(--fd-muted); white-space: nowrap; }
|
|
40
|
+
.fd-record-step { min-width: 34px; padding-inline: 8px; font-size: 18px; line-height: 1; }
|
|
41
|
+
.fd-record-step[aria-disabled="true"] { opacity: 0.5; cursor: default; }
|
|
42
|
+
/* ‹ and › are mirrored by the browser itself in a page read right to left: they point the way it reads. */
|
|
43
|
+
@container fd-form (max-width: 520px) {
|
|
44
|
+
.fd-record-bar { padding-inline: 12px; gap: 6px 8px; }
|
|
45
|
+
/* On a phone the gear says nothing more than its picture, and the trail folds to the page before this one, a way back. */
|
|
46
|
+
.fd-record-gear-words { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
|
|
47
|
+
.fd-breadcrumbs ol { flex-wrap: nowrap; font-size: 15px; }
|
|
48
|
+
.fd-breadcrumbs li:not(:nth-last-child(-n + 2)) { display: none; }
|
|
49
|
+
.fd-breadcrumbs li:nth-last-child(2)::before { content: "‹"; padding-inline: 0 6px; font-size: 20px; color: var(--fd-accent); }
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* The attachment beside the sheet: the sheet and its side panel in the first column, the file in the second. */
|
|
53
|
+
.fd-attachment-preview { display: grid; grid-template-rows: auto minmax(0, 1fr); min-width: 0; background: var(--fd-surface); border: 1px solid var(--fd-border); border-radius: var(--fd-radius); overflow: hidden; }
|
|
54
|
+
.fd-attachment-preview[hidden] { display: none; }
|
|
55
|
+
.fd-attachment-head { display: flex; align-items: center; gap: 6px; padding: 6px 8px 6px 12px; padding-inline: 12px 8px; border-block-end: 1px solid var(--fd-border); min-width: 0; }
|
|
56
|
+
.fd-attachment-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
|
|
57
|
+
.fd-attachment-count { color: var(--fd-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
|
|
58
|
+
.fd-attachment-head [hidden] { display: none; }
|
|
59
|
+
.fd-attachment-body { display: grid; min-height: 0; background: var(--fd-page); }
|
|
60
|
+
.fd-attachment-body > iframe { width: 100%; height: 100%; min-height: 70vh; border: 0; background: #fff; }
|
|
61
|
+
.fd-attachment-body > img { display: block; max-width: 100%; max-height: 80vh; margin: auto; object-fit: contain; }
|
|
62
|
+
.fd-sheet-layout.fd-has-preview:has(> .fd-attachment-preview:not([hidden])) { max-width: none; grid-template-columns: minmax(0, 3fr) minmax(320px, 2fr); grid-template-rows: auto 1fr; }
|
|
63
|
+
.fd-sheet-layout.fd-has-preview:has(> .fd-attachment-preview:not([hidden])) > :is(.fd-card, .fd-side) { grid-column: 1; }
|
|
64
|
+
.fd-sheet-layout.fd-has-preview > .fd-attachment-preview { grid-column: 2; grid-row: 1 / span 2; position: sticky; top: 8px; height: calc(100vh - 16px); max-height: 1200px; }
|
|
65
|
+
@container (max-width: 1000px) {
|
|
66
|
+
.fd-sheet-layout.fd-has-preview:has(> .fd-attachment-preview:not([hidden])) { grid-template-columns: minmax(0, 1fr); }
|
|
67
|
+
.fd-sheet-layout.fd-has-preview > .fd-attachment-preview { grid-column: 1; grid-row: auto; position: static; height: auto; }
|
|
68
|
+
.fd-attachment-body > iframe { min-height: 60vh; }
|
|
69
|
+
}
|
|
70
|
+
/* A side panel kept beside the sheet: narrower under 1000px, under the sheet only once the form is too narrow for both. */
|
|
71
|
+
@container (max-width: 1000px) { .fd-sheet-layout.fd-has-side[data-side-beside="always"]:not(:has(> .fd-attachment-preview:not([hidden]))) { grid-template-columns: minmax(0, 1fr) minmax(240px, 300px); } }
|
|
72
|
+
@container (max-width: 600px) { .fd-sheet-layout.fd-has-side[data-side-beside="always"]:not(:has(> .fd-attachment-preview:not([hidden]))) { grid-template-columns: minmax(0, 1fr); } }
|
|
73
|
+
`;
|
|
74
|
+
/** Puts the record's surroundings' look in the document once. */
|
|
75
|
+
function installRecordStyles(doc) {
|
|
76
|
+
if (doc.getElementById('fd-record-styles'))
|
|
77
|
+
return;
|
|
78
|
+
const style = doc.createElement('style');
|
|
79
|
+
style.id = 'fd-record-styles';
|
|
80
|
+
style.textContent = exports.RECORD_STYLES;
|
|
81
|
+
(doc.head ?? doc.documentElement).append(style);
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=record-styles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"record-styles.js","sourceRoot":"","sources":["../../../../../libs/viewer/src/lib/record-styles.ts"],"names":[],"mappings":";;;AAuEA,kDAMC;AA7ED;;;;;GAKG;AACU,QAAA,aAAa,GAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8DtC,CAAC;AAEF,iEAAiE;AACjE,SAAgB,mBAAmB,CAAC,GAAa;IAC/C,IAAI,GAAG,CAAC,cAAc,CAAC,kBAAkB,CAAC;QAAE,OAAO;IACnD,MAAM,KAAK,GAAG,GAAG,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IACzC,KAAK,CAAC,EAAE,GAAG,kBAAkB,CAAC;IAC9B,KAAK,CAAC,WAAW,GAAG,qBAAa,CAAC;IAClC,CAAC,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,eAAe,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAClD,CAAC"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What sits around a record, on the same tokens as the form: the bar over it
|
|
3
|
+
* (breadcrumbs, the gear menu, the pager), the attachment preview beside its
|
|
4
|
+
* sheet, and where the side panel goes. Put in the document after Fieldia's
|
|
5
|
+
* own stylesheet, so its rules on the sheet's layout come after those.
|
|
6
|
+
*/
|
|
7
|
+
export const RECORD_STYLES = /* css */ `
|
|
8
|
+
.fd-record-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; min-height: 46px; box-sizing: border-box; padding: 6px 16px; background: var(--fd-surface); border-block-end: 1px solid var(--fd-border); }
|
|
9
|
+
.fd-form > .fd-content > .fd-record-bar { margin: -4px 0 12px; border-radius: var(--fd-radius); border: 1px solid var(--fd-border); }
|
|
10
|
+
.fd-breadcrumbs { min-width: 0; }
|
|
11
|
+
.fd-breadcrumbs ol { display: flex; flex-wrap: wrap; align-items: center; list-style: none; margin: 0; padding: 0; font-size: 17px; line-height: 1.3; }
|
|
12
|
+
.fd-breadcrumbs li { display: flex; align-items: center; min-width: 0; }
|
|
13
|
+
.fd-breadcrumbs li + li::before { content: "/"; color: var(--fd-muted); padding-inline: 8px; }
|
|
14
|
+
.fd-crumb { all: unset; cursor: pointer; color: var(--fd-accent); border-radius: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
15
|
+
.fd-crumb:hover { text-decoration: underline; }
|
|
16
|
+
.fd-crumb:focus-visible { outline: 2px solid var(--fd-focus); outline-offset: 2px; }
|
|
17
|
+
.fd-crumb-current { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
18
|
+
.fd-record-actions { position: relative; }
|
|
19
|
+
.fd-record-actions[hidden] { display: none; }
|
|
20
|
+
.fd-record-gear { display: inline-flex; align-items: center; gap: 6px; }
|
|
21
|
+
.fd-record-gear svg { width: 16px; height: 16px; flex: none; }
|
|
22
|
+
.fd-record-menu { position: absolute; z-index: 40; inset-block-start: calc(100% + 4px); inset-inline-start: 0; display: grid; min-width: 220px; max-width: min(320px, 90vw); padding: 4px; box-sizing: border-box; background: var(--fd-surface); color: var(--fd-text); border: 1px solid var(--fd-border); border-radius: max(var(--fd-control-radius), 4px); box-shadow: 0 8px 24px rgba(0, 0, 0, 0.16); }
|
|
23
|
+
.fd-record-menu[data-end] { inset-inline-start: auto; inset-inline-end: 0; }
|
|
24
|
+
.fd-record-menu[hidden], .fd-record-menu [hidden] { display: none; }
|
|
25
|
+
.fd-record-menu [role="group"] { display: grid; }
|
|
26
|
+
.fd-record-menu-heading { padding: 6px 12px 2px; font-size: 12px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var(--fd-muted); }
|
|
27
|
+
.fd-record-menu-rule { margin: 4px 0; border-block-start: 1px solid var(--fd-border); }
|
|
28
|
+
.fd-record-menu-item { all: unset; box-sizing: border-box; display: flex; align-items: center; gap: 8px; min-height: 34px; padding: 6px 12px; border-radius: 3px; cursor: pointer; color: var(--fd-text); }
|
|
29
|
+
.fd-record-menu-item svg { width: 16px; height: 16px; flex: none; color: var(--fd-muted); }
|
|
30
|
+
.fd-record-menu-item:hover, .fd-record-menu-item:focus { background: var(--fd-accent-soft); }
|
|
31
|
+
.fd-record-menu-item:focus-visible { outline: 2px solid var(--fd-focus); outline-offset: -2px; }
|
|
32
|
+
.fd-record-menu-danger { color: var(--fd-error); }
|
|
33
|
+
.fd-record-pager { display: flex; align-items: center; gap: 4px; margin-inline-start: auto; }
|
|
34
|
+
.fd-record-pager[hidden] { display: none; }
|
|
35
|
+
.fd-record-pager-text { padding-inline-end: 6px; font-variant-numeric: tabular-nums; color: var(--fd-muted); white-space: nowrap; }
|
|
36
|
+
.fd-record-step { min-width: 34px; padding-inline: 8px; font-size: 18px; line-height: 1; }
|
|
37
|
+
.fd-record-step[aria-disabled="true"] { opacity: 0.5; cursor: default; }
|
|
38
|
+
/* ‹ and › are mirrored by the browser itself in a page read right to left: they point the way it reads. */
|
|
39
|
+
@container fd-form (max-width: 520px) {
|
|
40
|
+
.fd-record-bar { padding-inline: 12px; gap: 6px 8px; }
|
|
41
|
+
/* On a phone the gear says nothing more than its picture, and the trail folds to the page before this one, a way back. */
|
|
42
|
+
.fd-record-gear-words { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
|
|
43
|
+
.fd-breadcrumbs ol { flex-wrap: nowrap; font-size: 15px; }
|
|
44
|
+
.fd-breadcrumbs li:not(:nth-last-child(-n + 2)) { display: none; }
|
|
45
|
+
.fd-breadcrumbs li:nth-last-child(2)::before { content: "‹"; padding-inline: 0 6px; font-size: 20px; color: var(--fd-accent); }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/* The attachment beside the sheet: the sheet and its side panel in the first column, the file in the second. */
|
|
49
|
+
.fd-attachment-preview { display: grid; grid-template-rows: auto minmax(0, 1fr); min-width: 0; background: var(--fd-surface); border: 1px solid var(--fd-border); border-radius: var(--fd-radius); overflow: hidden; }
|
|
50
|
+
.fd-attachment-preview[hidden] { display: none; }
|
|
51
|
+
.fd-attachment-head { display: flex; align-items: center; gap: 6px; padding: 6px 8px 6px 12px; padding-inline: 12px 8px; border-block-end: 1px solid var(--fd-border); min-width: 0; }
|
|
52
|
+
.fd-attachment-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
|
|
53
|
+
.fd-attachment-count { color: var(--fd-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
|
|
54
|
+
.fd-attachment-head [hidden] { display: none; }
|
|
55
|
+
.fd-attachment-body { display: grid; min-height: 0; background: var(--fd-page); }
|
|
56
|
+
.fd-attachment-body > iframe { width: 100%; height: 100%; min-height: 70vh; border: 0; background: #fff; }
|
|
57
|
+
.fd-attachment-body > img { display: block; max-width: 100%; max-height: 80vh; margin: auto; object-fit: contain; }
|
|
58
|
+
.fd-sheet-layout.fd-has-preview:has(> .fd-attachment-preview:not([hidden])) { max-width: none; grid-template-columns: minmax(0, 3fr) minmax(320px, 2fr); grid-template-rows: auto 1fr; }
|
|
59
|
+
.fd-sheet-layout.fd-has-preview:has(> .fd-attachment-preview:not([hidden])) > :is(.fd-card, .fd-side) { grid-column: 1; }
|
|
60
|
+
.fd-sheet-layout.fd-has-preview > .fd-attachment-preview { grid-column: 2; grid-row: 1 / span 2; position: sticky; top: 8px; height: calc(100vh - 16px); max-height: 1200px; }
|
|
61
|
+
@container (max-width: 1000px) {
|
|
62
|
+
.fd-sheet-layout.fd-has-preview:has(> .fd-attachment-preview:not([hidden])) { grid-template-columns: minmax(0, 1fr); }
|
|
63
|
+
.fd-sheet-layout.fd-has-preview > .fd-attachment-preview { grid-column: 1; grid-row: auto; position: static; height: auto; }
|
|
64
|
+
.fd-attachment-body > iframe { min-height: 60vh; }
|
|
65
|
+
}
|
|
66
|
+
/* A side panel kept beside the sheet: narrower under 1000px, under the sheet only once the form is too narrow for both. */
|
|
67
|
+
@container (max-width: 1000px) { .fd-sheet-layout.fd-has-side[data-side-beside="always"]:not(:has(> .fd-attachment-preview:not([hidden]))) { grid-template-columns: minmax(0, 1fr) minmax(240px, 300px); } }
|
|
68
|
+
@container (max-width: 600px) { .fd-sheet-layout.fd-has-side[data-side-beside="always"]:not(:has(> .fd-attachment-preview:not([hidden]))) { grid-template-columns: minmax(0, 1fr); } }
|
|
69
|
+
`;
|
|
70
|
+
/** Puts the record's surroundings' look in the document once. */
|
|
71
|
+
export function installRecordStyles(doc) {
|
|
72
|
+
if (doc.getElementById('fd-record-styles'))
|
|
73
|
+
return;
|
|
74
|
+
const style = doc.createElement('style');
|
|
75
|
+
style.id = 'fd-record-styles';
|
|
76
|
+
style.textContent = RECORD_STYLES;
|
|
77
|
+
(doc.head ?? doc.documentElement).append(style);
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=record-styles.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"record-styles.mjs","sourceRoot":"","sources":["../../../../../libs/viewer/src/lib/record-styles.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,SAAS,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8DtC,CAAC;AAEF,iEAAiE;AACjE,MAAM,UAAU,mBAAmB,CAAC,GAAa;IAC/C,IAAI,GAAG,CAAC,cAAc,CAAC,kBAAkB,CAAC;QAAE,OAAO;IACnD,MAAM,KAAK,GAAG,GAAG,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IACzC,KAAK,CAAC,EAAE,GAAG,kBAAkB,CAAC;IAC9B,KAAK,CAAC,WAAW,GAAG,aAAa,CAAC;IAClC,CAAC,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,eAAe,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAClD,CAAC"}
|
package/src/lib/related.d.mts
CHANGED
|
@@ -1,7 +1,30 @@
|
|
|
1
|
-
import type { Page } from '@fieldia/core';
|
|
1
|
+
import type { Page, Tone } from '@fieldia/core';
|
|
2
2
|
import type { WidgetDialogs } from '@fieldia/widgets';
|
|
3
3
|
import type { ViewerOptions } from './viewer.mjs';
|
|
4
4
|
/** The field a page's records are named by: its title, or "name", or its first line of text. */
|
|
5
5
|
export declare function nameFieldOf(page: Page): string | null;
|
|
6
|
-
/**
|
|
7
|
-
|
|
6
|
+
/**
|
|
7
|
+
* What a part asks the app's pages for: the page for a model, to show a
|
|
8
|
+
* linked record in a dialog; or a saved page by its id — and the version it
|
|
9
|
+
* keeps to, when it keeps to one — to draw a saved form placed in another.
|
|
10
|
+
*/
|
|
11
|
+
export type PageRequest = {
|
|
12
|
+
model: string;
|
|
13
|
+
} | {
|
|
14
|
+
id: string;
|
|
15
|
+
version?: number;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* The app's pages: a record of them by key — a model, a page's id, or
|
|
19
|
+
* `id@version` for a version kept to — or a function asked for each, which
|
|
20
|
+
* may answer at once or with a promise.
|
|
21
|
+
*/
|
|
22
|
+
export type PageFinder = Record<string, Page> | ((request: PageRequest) => Page | null | undefined | Promise<Page | null | undefined>);
|
|
23
|
+
/** The page the app gives for a request: from `pages`, and for a model from `relatedPages` after it. A promise when it answers later. */
|
|
24
|
+
export declare function findPage(options: Pick<ViewerOptions, 'pages' | 'relatedPages'>, request: PageRequest): Page | null | Promise<Page | null>;
|
|
25
|
+
/**
|
|
26
|
+
* The dialogs a page's widgets may open, made from the viewer's own options.
|
|
27
|
+
* A linked record's page says its words through `say`, the opener's, so they
|
|
28
|
+
* show over the dialog and outlive it.
|
|
29
|
+
*/
|
|
30
|
+
export declare function pageDialogs(options: ViewerOptions, say?: (message: string, tone: Tone) => void): WidgetDialogs;
|
package/src/lib/related.d.ts
CHANGED
|
@@ -1,7 +1,30 @@
|
|
|
1
|
-
import type { Page } from '@fieldia/core';
|
|
1
|
+
import type { Page, Tone } from '@fieldia/core';
|
|
2
2
|
import type { WidgetDialogs } from '@fieldia/widgets';
|
|
3
3
|
import type { ViewerOptions } from './viewer';
|
|
4
4
|
/** The field a page's records are named by: its title, or "name", or its first line of text. */
|
|
5
5
|
export declare function nameFieldOf(page: Page): string | null;
|
|
6
|
-
/**
|
|
7
|
-
|
|
6
|
+
/**
|
|
7
|
+
* What a part asks the app's pages for: the page for a model, to show a
|
|
8
|
+
* linked record in a dialog; or a saved page by its id — and the version it
|
|
9
|
+
* keeps to, when it keeps to one — to draw a saved form placed in another.
|
|
10
|
+
*/
|
|
11
|
+
export type PageRequest = {
|
|
12
|
+
model: string;
|
|
13
|
+
} | {
|
|
14
|
+
id: string;
|
|
15
|
+
version?: number;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* The app's pages: a record of them by key — a model, a page's id, or
|
|
19
|
+
* `id@version` for a version kept to — or a function asked for each, which
|
|
20
|
+
* may answer at once or with a promise.
|
|
21
|
+
*/
|
|
22
|
+
export type PageFinder = Record<string, Page> | ((request: PageRequest) => Page | null | undefined | Promise<Page | null | undefined>);
|
|
23
|
+
/** The page the app gives for a request: from `pages`, and for a model from `relatedPages` after it. A promise when it answers later. */
|
|
24
|
+
export declare function findPage(options: Pick<ViewerOptions, 'pages' | 'relatedPages'>, request: PageRequest): Page | null | Promise<Page | null>;
|
|
25
|
+
/**
|
|
26
|
+
* The dialogs a page's widgets may open, made from the viewer's own options.
|
|
27
|
+
* A linked record's page says its words through `say`, the opener's, so they
|
|
28
|
+
* show over the dialog and outlive it.
|
|
29
|
+
*/
|
|
30
|
+
export declare function pageDialogs(options: ViewerOptions, say?: (message: string, tone: Tone) => void): WidgetDialogs;
|
package/src/lib/related.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.nameFieldOf = nameFieldOf;
|
|
4
|
+
exports.findPage = findPage;
|
|
4
5
|
exports.pageDialogs = pageDialogs;
|
|
5
6
|
const dialog_1 = require("./dialog");
|
|
6
7
|
/** The field a page's records are named by: its title, or "name", or its first line of text. */
|
|
@@ -11,8 +12,8 @@ function nameFieldOf(page) {
|
|
|
11
12
|
return 'name';
|
|
12
13
|
return Object.entries(page.fields).find(([, def]) => def.type === 'char')?.[0] ?? null;
|
|
13
14
|
}
|
|
14
|
-
/** A form made of fields, for values edited in a dialog (a line, for one). */
|
|
15
|
-
function valuesPage(fields, title, readonly) {
|
|
15
|
+
/** A form made of fields, for values edited in a dialog (a line, for one): laid out as asked (a line's own form), else every field in two columns. */
|
|
16
|
+
function valuesPage(fields, title, readonly, layout) {
|
|
16
17
|
const shown = Object.fromEntries(Object.entries(fields).map(([name, def]) => [name, readonly ? { ...def, readonly: true } : def]));
|
|
17
18
|
return {
|
|
18
19
|
fieldia: '0.1',
|
|
@@ -23,10 +24,21 @@ function valuesPage(fields, title, readonly) {
|
|
|
23
24
|
layout: {
|
|
24
25
|
type: 'sections',
|
|
25
26
|
id: 'values',
|
|
26
|
-
children: [{ type: 'section', id: 'values-section', columns: 2, children: Object.keys(shown).map((name) => ({ type: 'field', id: `values-${name}`, field: name })) }],
|
|
27
|
+
children: layout ?? [{ type: 'section', id: 'values-section', columns: 2, children: Object.keys(shown).map((name) => ({ type: 'field', id: `values-${name}`, field: name })) }],
|
|
27
28
|
},
|
|
28
29
|
};
|
|
29
30
|
}
|
|
31
|
+
/** The page the app gives for a request: from `pages`, and for a model from `relatedPages` after it. A promise when it answers later. */
|
|
32
|
+
function findPage(options, request) {
|
|
33
|
+
const { pages, relatedPages } = options;
|
|
34
|
+
const key = 'model' in request ? request.model : request.version === undefined ? request.id : `${request.id}@${request.version}`;
|
|
35
|
+
const found = typeof pages === 'function' ? pages(request) : pages && Object.prototype.hasOwnProperty.call(pages, key) ? pages[key] : null;
|
|
36
|
+
if (found instanceof Promise)
|
|
37
|
+
return found.then((page) => page ?? null);
|
|
38
|
+
if (found || !('model' in request))
|
|
39
|
+
return found ?? null;
|
|
40
|
+
return (typeof relatedPages === 'function' ? relatedPages(request.model) : relatedPages?.[request.model]) ?? null;
|
|
41
|
+
}
|
|
30
42
|
/** Only finding and making linked records: values edited in a dialog are no record of the source's to load, save or recalculate. */
|
|
31
43
|
function lookupsOf(source) {
|
|
32
44
|
if (!source)
|
|
@@ -36,9 +48,13 @@ function lookupsOf(source) {
|
|
|
36
48
|
...(source.create ? { create: (request) => source.create(request) } : {}),
|
|
37
49
|
};
|
|
38
50
|
}
|
|
39
|
-
/**
|
|
40
|
-
|
|
41
|
-
|
|
51
|
+
/**
|
|
52
|
+
* The dialogs a page's widgets may open, made from the viewer's own options.
|
|
53
|
+
* A linked record's page says its words through `say`, the opener's, so they
|
|
54
|
+
* show over the dialog and outlive it.
|
|
55
|
+
*/
|
|
56
|
+
function pageDialogs(options, say) {
|
|
57
|
+
const pageFor = (model) => findPage(options, { model });
|
|
42
58
|
// A dialog's page looks and reads like the page that opened it, and can open dialogs of its own.
|
|
43
59
|
const shared = {
|
|
44
60
|
locale: options.locale,
|
|
@@ -46,14 +62,18 @@ function pageDialogs(options) {
|
|
|
46
62
|
dir: options.dir,
|
|
47
63
|
widgets: options.widgets,
|
|
48
64
|
preferences: options.preferences,
|
|
65
|
+
pages: options.pages,
|
|
49
66
|
relatedPages: options.relatedPages,
|
|
50
67
|
translate: options.translate,
|
|
51
68
|
look: options.page.look,
|
|
69
|
+
// The person, and the values the app passes in, as the opener's own conditions and filters read them.
|
|
70
|
+
user: options.user,
|
|
71
|
+
context: options.context,
|
|
52
72
|
};
|
|
53
73
|
return {
|
|
54
74
|
canOpen: (model) => pageFor(model) !== null,
|
|
55
75
|
async openRecord(model, request) {
|
|
56
|
-
const related = pageFor(model);
|
|
76
|
+
const related = await pageFor(model);
|
|
57
77
|
if (!related)
|
|
58
78
|
return null;
|
|
59
79
|
const nameField = nameFieldOf(related);
|
|
@@ -62,25 +82,32 @@ function pageDialogs(options) {
|
|
|
62
82
|
page: related,
|
|
63
83
|
dataSource: options.dataSource,
|
|
64
84
|
recordId: request.recordId ?? null,
|
|
65
|
-
values: request.name && nameField ? { [nameField]: request.name } : undefined,
|
|
85
|
+
values: request.name || request.values ? { ...(request.values ?? {}), ...(request.name && nameField ? { [nameField]: request.name } : {}) } : undefined,
|
|
66
86
|
title: request.title,
|
|
67
87
|
size: 'large',
|
|
88
|
+
...(say ? { host: { say } } : {}),
|
|
68
89
|
});
|
|
69
90
|
if (!result.saved || result.recordId === null)
|
|
70
91
|
return null;
|
|
71
92
|
const name = nameField ? result.values[nameField] : null;
|
|
72
|
-
|
|
93
|
+
const label = typeof name === 'string' && name ? name : request.name ?? request.title;
|
|
94
|
+
// Its values as its page saved them, for a line of a table that opened it to take back.
|
|
95
|
+
return request.withValues ? { id: result.recordId, label, values: result.values } : { id: result.recordId, label };
|
|
73
96
|
},
|
|
74
97
|
searchMore: (request) => (0, dialog_1.openSearchDialog)({ title: request.title, search: request.search, locale: options.locale, skin: options.skin, dir: options.dir, look: options.page.look }),
|
|
75
98
|
async editValues(request) {
|
|
76
99
|
const result = await (0, dialog_1.openFormDialog)({
|
|
77
100
|
...shared,
|
|
78
|
-
page: valuesPage(request.fields, request.title, request.readonly === true),
|
|
101
|
+
page: valuesPage(request.fields, request.title, request.readonly === true, request.layout),
|
|
102
|
+
...(request.parent ? { parent: request.parent } : {}),
|
|
79
103
|
dataSource: lookupsOf(options.dataSource),
|
|
80
104
|
values: request.values,
|
|
81
105
|
title: request.title,
|
|
106
|
+
// A line's own form is laid out as a page is: the room of a record's dialog.
|
|
107
|
+
...(request.layout ? { size: 'large' } : {}),
|
|
82
108
|
mode: 'values',
|
|
83
109
|
...(request.recompute ? { recompute: request.recompute } : {}),
|
|
110
|
+
...(say ? { host: { say } } : {}),
|
|
84
111
|
});
|
|
85
112
|
return result.saved ? result.values : null;
|
|
86
113
|
},
|
package/src/lib/related.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"related.js","sourceRoot":"","sources":["../../../../../libs/viewer/src/lib/related.ts"],"names":[],"mappings":";;AAMA,kCAIC;
|
|
1
|
+
{"version":3,"file":"related.js","sourceRoot":"","sources":["../../../../../libs/viewer/src/lib/related.ts"],"names":[],"mappings":"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"}
|
package/src/lib/related.mjs
CHANGED
|
@@ -7,8 +7,8 @@ export function nameFieldOf(page) {
|
|
|
7
7
|
return 'name';
|
|
8
8
|
return Object.entries(page.fields).find(([, def]) => def.type === 'char')?.[0] ?? null;
|
|
9
9
|
}
|
|
10
|
-
/** A form made of fields, for values edited in a dialog (a line, for one). */
|
|
11
|
-
function valuesPage(fields, title, readonly) {
|
|
10
|
+
/** A form made of fields, for values edited in a dialog (a line, for one): laid out as asked (a line's own form), else every field in two columns. */
|
|
11
|
+
function valuesPage(fields, title, readonly, layout) {
|
|
12
12
|
const shown = Object.fromEntries(Object.entries(fields).map(([name, def]) => [name, readonly ? { ...def, readonly: true } : def]));
|
|
13
13
|
return {
|
|
14
14
|
fieldia: '0.1',
|
|
@@ -19,10 +19,21 @@ function valuesPage(fields, title, readonly) {
|
|
|
19
19
|
layout: {
|
|
20
20
|
type: 'sections',
|
|
21
21
|
id: 'values',
|
|
22
|
-
children: [{ type: 'section', id: 'values-section', columns: 2, children: Object.keys(shown).map((name) => ({ type: 'field', id: `values-${name}`, field: name })) }],
|
|
22
|
+
children: layout ?? [{ type: 'section', id: 'values-section', columns: 2, children: Object.keys(shown).map((name) => ({ type: 'field', id: `values-${name}`, field: name })) }],
|
|
23
23
|
},
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
|
+
/** The page the app gives for a request: from `pages`, and for a model from `relatedPages` after it. A promise when it answers later. */
|
|
27
|
+
export function findPage(options, request) {
|
|
28
|
+
const { pages, relatedPages } = options;
|
|
29
|
+
const key = 'model' in request ? request.model : request.version === undefined ? request.id : `${request.id}@${request.version}`;
|
|
30
|
+
const found = typeof pages === 'function' ? pages(request) : pages && Object.prototype.hasOwnProperty.call(pages, key) ? pages[key] : null;
|
|
31
|
+
if (found instanceof Promise)
|
|
32
|
+
return found.then((page) => page ?? null);
|
|
33
|
+
if (found || !('model' in request))
|
|
34
|
+
return found ?? null;
|
|
35
|
+
return (typeof relatedPages === 'function' ? relatedPages(request.model) : relatedPages?.[request.model]) ?? null;
|
|
36
|
+
}
|
|
26
37
|
/** Only finding and making linked records: values edited in a dialog are no record of the source's to load, save or recalculate. */
|
|
27
38
|
function lookupsOf(source) {
|
|
28
39
|
if (!source)
|
|
@@ -32,9 +43,13 @@ function lookupsOf(source) {
|
|
|
32
43
|
...(source.create ? { create: (request) => source.create(request) } : {}),
|
|
33
44
|
};
|
|
34
45
|
}
|
|
35
|
-
/**
|
|
36
|
-
|
|
37
|
-
|
|
46
|
+
/**
|
|
47
|
+
* The dialogs a page's widgets may open, made from the viewer's own options.
|
|
48
|
+
* A linked record's page says its words through `say`, the opener's, so they
|
|
49
|
+
* show over the dialog and outlive it.
|
|
50
|
+
*/
|
|
51
|
+
export function pageDialogs(options, say) {
|
|
52
|
+
const pageFor = (model) => findPage(options, { model });
|
|
38
53
|
// A dialog's page looks and reads like the page that opened it, and can open dialogs of its own.
|
|
39
54
|
const shared = {
|
|
40
55
|
locale: options.locale,
|
|
@@ -42,14 +57,18 @@ export function pageDialogs(options) {
|
|
|
42
57
|
dir: options.dir,
|
|
43
58
|
widgets: options.widgets,
|
|
44
59
|
preferences: options.preferences,
|
|
60
|
+
pages: options.pages,
|
|
45
61
|
relatedPages: options.relatedPages,
|
|
46
62
|
translate: options.translate,
|
|
47
63
|
look: options.page.look,
|
|
64
|
+
// The person, and the values the app passes in, as the opener's own conditions and filters read them.
|
|
65
|
+
user: options.user,
|
|
66
|
+
context: options.context,
|
|
48
67
|
};
|
|
49
68
|
return {
|
|
50
69
|
canOpen: (model) => pageFor(model) !== null,
|
|
51
70
|
async openRecord(model, request) {
|
|
52
|
-
const related = pageFor(model);
|
|
71
|
+
const related = await pageFor(model);
|
|
53
72
|
if (!related)
|
|
54
73
|
return null;
|
|
55
74
|
const nameField = nameFieldOf(related);
|
|
@@ -58,25 +77,32 @@ export function pageDialogs(options) {
|
|
|
58
77
|
page: related,
|
|
59
78
|
dataSource: options.dataSource,
|
|
60
79
|
recordId: request.recordId ?? null,
|
|
61
|
-
values: request.name && nameField ? { [nameField]: request.name } : undefined,
|
|
80
|
+
values: request.name || request.values ? { ...(request.values ?? {}), ...(request.name && nameField ? { [nameField]: request.name } : {}) } : undefined,
|
|
62
81
|
title: request.title,
|
|
63
82
|
size: 'large',
|
|
83
|
+
...(say ? { host: { say } } : {}),
|
|
64
84
|
});
|
|
65
85
|
if (!result.saved || result.recordId === null)
|
|
66
86
|
return null;
|
|
67
87
|
const name = nameField ? result.values[nameField] : null;
|
|
68
|
-
|
|
88
|
+
const label = typeof name === 'string' && name ? name : request.name ?? request.title;
|
|
89
|
+
// Its values as its page saved them, for a line of a table that opened it to take back.
|
|
90
|
+
return request.withValues ? { id: result.recordId, label, values: result.values } : { id: result.recordId, label };
|
|
69
91
|
},
|
|
70
92
|
searchMore: (request) => openSearchDialog({ title: request.title, search: request.search, locale: options.locale, skin: options.skin, dir: options.dir, look: options.page.look }),
|
|
71
93
|
async editValues(request) {
|
|
72
94
|
const result = await openFormDialog({
|
|
73
95
|
...shared,
|
|
74
|
-
page: valuesPage(request.fields, request.title, request.readonly === true),
|
|
96
|
+
page: valuesPage(request.fields, request.title, request.readonly === true, request.layout),
|
|
97
|
+
...(request.parent ? { parent: request.parent } : {}),
|
|
75
98
|
dataSource: lookupsOf(options.dataSource),
|
|
76
99
|
values: request.values,
|
|
77
100
|
title: request.title,
|
|
101
|
+
// A line's own form is laid out as a page is: the room of a record's dialog.
|
|
102
|
+
...(request.layout ? { size: 'large' } : {}),
|
|
78
103
|
mode: 'values',
|
|
79
104
|
...(request.recompute ? { recompute: request.recompute } : {}),
|
|
105
|
+
...(say ? { host: { say } } : {}),
|
|
80
106
|
});
|
|
81
107
|
return result.saved ? result.values : null;
|
|
82
108
|
},
|
package/src/lib/related.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"related.mjs","sourceRoot":"","sources":["../../../../../libs/viewer/src/lib/related.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAG5D,gGAAgG;AAChG,MAAM,UAAU,WAAW,CAAC,IAAU;IACpC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,OAAO,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK;QAAE,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC;IAC7F,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;QAAE,OAAO,MAAM,CAAC;IACvC,OAAO,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AACzF,CAAC;AAID,
|
|
1
|
+
{"version":3,"file":"related.mjs","sourceRoot":"","sources":["../../../../../libs/viewer/src/lib/related.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Tone } from '@fieldia/core';
|
|
2
|
+
import type { El } from './dom.mjs';
|
|
3
|
+
/**
|
|
4
|
+
* Words a step says, as toasts at the foot of the screen: each in its tone,
|
|
5
|
+
* one over another, in a polite live region inside the form — so they wear its
|
|
6
|
+
* skin, scheme and direction. Each goes by itself after a moment, but not
|
|
7
|
+
* while the pointer or the focus is on it, and at once with its ×. They show
|
|
8
|
+
* over a dialog or panel too, and while a modal one is open, a screen reader —
|
|
9
|
+
* which hears only inside it — is told them there as well, unseen.
|
|
10
|
+
*/
|
|
11
|
+
export declare function sayer(root: HTMLElement, el: El, dismiss: string): (message: string, tone: Tone) => void;
|
package/src/lib/say.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Tone } from '@fieldia/core';
|
|
2
|
+
import type { El } from './dom';
|
|
3
|
+
/**
|
|
4
|
+
* Words a step says, as toasts at the foot of the screen: each in its tone,
|
|
5
|
+
* one over another, in a polite live region inside the form — so they wear its
|
|
6
|
+
* skin, scheme and direction. Each goes by itself after a moment, but not
|
|
7
|
+
* while the pointer or the focus is on it, and at once with its ×. They show
|
|
8
|
+
* over a dialog or panel too, and while a modal one is open, a screen reader —
|
|
9
|
+
* which hears only inside it — is told them there as well, unseen.
|
|
10
|
+
*/
|
|
11
|
+
export declare function sayer(root: HTMLElement, el: El, dismiss: string): (message: string, tone: Tone) => void;
|
package/src/lib/say.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sayer = sayer;
|
|
4
|
+
/** How long words stay before they go by themselves: a warning or a danger stays longer. */
|
|
5
|
+
const STAY_MS = 6000;
|
|
6
|
+
const STAY_LONG_MS = 10000;
|
|
7
|
+
/** The most toasts on screen at once: the oldest give way. */
|
|
8
|
+
const MOST = 4;
|
|
9
|
+
/**
|
|
10
|
+
* Words a step says, as toasts at the foot of the screen: each in its tone,
|
|
11
|
+
* one over another, in a polite live region inside the form — so they wear its
|
|
12
|
+
* skin, scheme and direction. Each goes by itself after a moment, but not
|
|
13
|
+
* while the pointer or the focus is on it, and at once with its ×. They show
|
|
14
|
+
* over a dialog or panel too, and while a modal one is open, a screen reader —
|
|
15
|
+
* which hears only inside it — is told them there as well, unseen.
|
|
16
|
+
*/
|
|
17
|
+
function sayer(root, el, dismiss) {
|
|
18
|
+
// There before anything is said, so a screen reader hears what comes into it.
|
|
19
|
+
const region = el('div', { class: 'fd-says', role: 'status', 'aria-live': 'polite' });
|
|
20
|
+
root.append(region);
|
|
21
|
+
return (message, tone) => {
|
|
22
|
+
const close = el('button', { type: 'button', class: 'fd-alert-close', 'aria-label': dismiss }, '×');
|
|
23
|
+
const toast = el('div', { class: 'fd-say', 'data-tone': tone }, el('span', {}, message), close);
|
|
24
|
+
// Its edge in its tone's colour, from the scheme's own: a danger is the error colour.
|
|
25
|
+
toast.style.setProperty('--fd-tone', `var(--fd-${tone === 'danger' ? 'error' : tone})`);
|
|
26
|
+
let timer;
|
|
27
|
+
const hold = () => clearTimeout(timer);
|
|
28
|
+
const go = () => {
|
|
29
|
+
hold();
|
|
30
|
+
toast.remove();
|
|
31
|
+
};
|
|
32
|
+
const wait = () => {
|
|
33
|
+
hold();
|
|
34
|
+
timer = setTimeout(go, tone === 'warning' || tone === 'danger' ? STAY_LONG_MS : STAY_MS);
|
|
35
|
+
};
|
|
36
|
+
close.addEventListener('click', go);
|
|
37
|
+
toast.addEventListener('mouseenter', hold);
|
|
38
|
+
toast.addEventListener('focusin', hold);
|
|
39
|
+
toast.addEventListener('mouseleave', wait);
|
|
40
|
+
toast.addEventListener('focusout', wait);
|
|
41
|
+
region.append(toast);
|
|
42
|
+
while (region.children.length > MOST)
|
|
43
|
+
region.firstElementChild?.remove();
|
|
44
|
+
echo(region, el, message);
|
|
45
|
+
wait();
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/** The words again, unseen, in a live region of the topmost modal dialog the region is not in. */
|
|
49
|
+
function echo(region, el, message) {
|
|
50
|
+
const modals = [...region.ownerDocument.querySelectorAll('[aria-modal="true"]')].filter((m) => !m.contains(region));
|
|
51
|
+
const top = modals[modals.length - 1];
|
|
52
|
+
if (!top)
|
|
53
|
+
return;
|
|
54
|
+
let heard = top.querySelector(':scope > .fd-says-echo');
|
|
55
|
+
if (!heard) {
|
|
56
|
+
heard = el('div', { class: 'fd-says-echo fd-sr-only', role: 'status', 'aria-live': 'polite' });
|
|
57
|
+
top.append(heard);
|
|
58
|
+
}
|
|
59
|
+
heard.textContent = message;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=say.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"say.js","sourceRoot":"","sources":["../../../../../libs/viewer/src/lib/say.ts"],"names":[],"mappings":"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"}
|