@museumwnf/viewer-core 2.6.0 → 2.8.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 +4 -3
- package/package.json +1 -1
- package/src/catalogue/fieldSearch.js +1 -1
- package/src/dxa/exhibition/useExhibitionItemDetail.js +3 -3
- package/src/dxa/exhibition/useExhibitionPartner.js +6 -5
- package/src/dxa/exhibition/useExhibitionSheet.js +11 -15
- package/src/dxa/exhibition/useExhibitionThemes.js +3 -1
- package/src/dxa/gallery/useGalleryItemDetail.js +4 -3
- package/src/dxa/gallery/useGallerySheet.js +11 -15
- package/src/index.js +1 -1
- package/src/record/derivations.js +30 -6
- package/src/record/recordSheet.js +18 -19
package/README.md
CHANGED
|
@@ -415,13 +415,14 @@ viewer-layout's.
|
|
|
415
415
|
|
|
416
416
|
| Export | Meaning |
|
|
417
417
|
| --- | --- |
|
|
418
|
-
| `useRecordSheet(record, { entity, translations, attribution })` | what `useRecordLanguage` returns, plus `text` (the record's translation in the active language, falling back to English), `ready` (the entity and every named related entity loaded, in the active language and in English), `terms` (`[{ id, word, definition, spellings }]` for the record's `glossary_ids
|
|
418
|
+
| `useRecordSheet(record, { entity, translations, attribution })` | what `useRecordLanguage` returns, plus `text` (the record's translation in the active language, falling back to English), `ready` (the entity and every named related entity loaded, in the active language and in English), `terms` (`[{ id, word, definition, spellings }]` for the record's `glossary_ids` that have a row in the active language — none through another language), `glossary` (the `[{ id, spelling }]` list the renderers take) and `attribution` — when `attribution: ['author', 'copy_editor']` names fields that are proper names, the first other language of the record that has them supplies them (`{ from, author, … }`), for a site that asks; no platform sheet does, since legacy printed a language's own credits only. |
|
|
419
419
|
| `sheetRows(spec, ctx)` | `[{ key, label, render, value, html }]` from `[{ key, label, value, render, when, join }]`: `value` a function of `ctx` or the name of a field of `ctx.text`; `render` one of `inline`, `block`, `plain` (through the pipeline, with `ctx.glossary`), `link`, `custom` (handed back for a slot); `when(ctx)` gates the row; empty values are dropped. "Materials/techniques" on one site and "Type" on another for the same field are both legacy facts, and both are a spec entry. |
|
|
420
420
|
| `useGlossaryPopup(entries)` | `{ active, onClick, close }` — one delegated click on the sheet's container answers the term rendered as `.gloss-term` |
|
|
421
|
-
| `searchGlossary(input, language)` | the terms whose spelling starts with the input, for a glossary search box |
|
|
421
|
+
| `searchGlossary(input, language)` | the terms with a row in `language` whose spelling starts with the input, for a glossary search box |
|
|
422
422
|
| `glossaryTermsForText(text, language, { entity })` | `glossaryTermsFor`'s counterpart for a text with no `glossary_ids` column — a dynasty history, a theme essay: `[{ id, word, definition, spellings }]` for every glossary term whose spelling occurs in `text`, matched the same word-bounded way `md`/`renderBlock` highlight it. Pass the result through `glossaryEntries` for the `glossary` option the renderers take, the same as `terms`/`glossary` from `useRecordSheet` |
|
|
423
423
|
| `citation({ author, name, project, publisher, year, permalink, inWord })` | the sentence under a sheet, assembled from parts in order rather than written into a text with holes; `permalink` is the page's own address, e.g. from [`sourceUrl()`](#the-declaration-outside-a-component) |
|
|
424
|
-
| `relatedRecords(record, { entity, language })` / `useRelatedRecords` | `{ inPackage: [{ reference, record, justification }], outside: [reference] }` — a reference the package does not hold stays a reference; nothing is dropped and nothing invented |
|
|
424
|
+
| `relatedRecords(record, { entity, language })` / `useRelatedRecords` | `{ inPackage: [{ reference, record, justification }], outside: [reference] }`, the justification in `language` only — a reference the package does not hold stays a reference; nothing is dropped and nothing invented |
|
|
425
|
+
| `pictureCredits(picture, language)` | `{ photographer, copyright }` of an item picture in `language`, from the package's per-language `photographers` / `copyrights` maps, as legacy kept and printed them; the flat `photographer` / `copyright` of a package exported before the maps |
|
|
425
426
|
| `timelineLinkFor(record, { name, keys, country, round })` | the route location of the timeline results for the record's country and dates, or null |
|
|
426
427
|
|
|
427
428
|
### Collection trees
|
package/package.json
CHANGED
|
@@ -28,7 +28,7 @@ export function searchFields({ dynastyLabel } = {}) {
|
|
|
28
28
|
location: (item, text) => [text.location, item.country_id],
|
|
29
29
|
provenance: (item, text) => text.provenance,
|
|
30
30
|
patron: (item, text) => text.patrons ?? text.initial_owner,
|
|
31
|
-
artist: (item, text) => [...(item.artist_names ?? []), text.architects],
|
|
31
|
+
artist: (item, text) => [...(item.artist_names ?? []), text.artist, text.architects],
|
|
32
32
|
material: (item, text) => text.type,
|
|
33
33
|
// The catch-all across the descriptive fields the others leave out.
|
|
34
34
|
other: (item, text) => [
|
|
@@ -22,7 +22,7 @@ import { useExhibitionSheet } from './useExhibitionSheet.js'
|
|
|
22
22
|
* The exhibition's item-page spec, over its data layer's `data` and its
|
|
23
23
|
* timeline layer's `timeline` (`useExhibitionTimeline`).
|
|
24
24
|
*
|
|
25
|
-
* Reads `data.
|
|
25
|
+
* Reads `data.dynastyById`, `data.isExploreRecord`,
|
|
26
26
|
* `data.isHiddenPartner`, `data.labelOf`, `data.partnerById`,
|
|
27
27
|
* `data.partnerRoute`, `data.translations`, what `useExhibitionSheet` reads,
|
|
28
28
|
* and `timeline.countryIdForCode`, `timeline.hasTimeline`,
|
|
@@ -30,7 +30,7 @@ import { useExhibitionSheet } from './useExhibitionSheet.js'
|
|
|
30
30
|
*/
|
|
31
31
|
export function useExhibitionItemDetail(data, timeline) {
|
|
32
32
|
const {
|
|
33
|
-
|
|
33
|
+
dynastyById, isExploreRecord, isHiddenPartner, labelOf, partnerById, partnerRoute, translations,
|
|
34
34
|
} = data
|
|
35
35
|
const { countryIdForCode, hasTimeline, timelineEvents, timelineSpec } = timeline
|
|
36
36
|
const { countries: timelineCountries, findEvents } = timelineEvents
|
|
@@ -41,7 +41,7 @@ export function useExhibitionItemDetail(data, timeline) {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
function dynastyTr(dynasty, language) {
|
|
44
|
-
return translations('dynasties', language)[dynasty.id] ??
|
|
44
|
+
return translations('dynasties', language)[dynasty.id] ?? {}
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
// Legacy popped out a dynasty only when it had a history to show.
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { pictureCredits } from '../../record/derivations.js'
|
|
2
|
+
|
|
1
3
|
// The partner list and profile, as specs. The grouping, the A–Z toggle, the
|
|
2
4
|
// tab strip's mechanics, the language switch and the media gallery are the
|
|
3
5
|
// platform's (`PartnerListView`/`RecordView`); what stays here is an
|
|
@@ -11,12 +13,12 @@
|
|
|
11
13
|
/**
|
|
12
14
|
* The exhibition's partner specs, over its data layer's `data`.
|
|
13
15
|
*
|
|
14
|
-
* Reads `data.
|
|
16
|
+
* Reads `data.isHiddenPartner`, `data.labelOf`,
|
|
15
17
|
* `data.partnerRoute`. `config` carries nothing today.
|
|
16
18
|
*/
|
|
17
19
|
export function useExhibitionPartner(data, config = {}) {
|
|
18
20
|
void config
|
|
19
|
-
const {
|
|
21
|
+
const { isHiddenPartner, labelOf, partnerRoute } = data
|
|
20
22
|
|
|
21
23
|
// ── The list (Partners.vue) ─────────────────────────────────────────
|
|
22
24
|
//
|
|
@@ -52,9 +54,8 @@ export function useExhibitionPartner(data, config = {}) {
|
|
|
52
54
|
.map((p) => ({
|
|
53
55
|
url: p.url,
|
|
54
56
|
alt: labelOf('partners', record.id),
|
|
55
|
-
caption: p.captions?.[language] ??
|
|
56
|
-
|
|
57
|
-
copyright: p.copyright ?? '',
|
|
57
|
+
caption: p.captions?.[language] ?? '',
|
|
58
|
+
...pictureCredits(p, language),
|
|
58
59
|
})),
|
|
59
60
|
}
|
|
60
61
|
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
// dynasties stand in when it has none, and keep their own popouts
|
|
28
28
|
// (decision D5, inventory-app#2233).
|
|
29
29
|
|
|
30
|
-
import { artistLine } from '../../record/derivations.js'
|
|
30
|
+
import { artistLine, pictureCredits, recordArtist } from '../../record/derivations.js'
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
33
|
* The exhibition's item-sheet spec, over its data layer's `data`.
|
|
@@ -44,7 +44,7 @@ export function useExhibitionSheet(data, config = {}) {
|
|
|
44
44
|
|
|
45
45
|
const dynastyNames = (c) =>
|
|
46
46
|
(c.record.dynasty_ids ?? [])
|
|
47
|
-
.map((id) => translations('dynasties', c.language)[id]?.name ??
|
|
47
|
+
.map((id) => translations('dynasties', c.language)[id]?.name ?? '')
|
|
48
48
|
.filter(Boolean)
|
|
49
49
|
.join(', ')
|
|
50
50
|
|
|
@@ -61,7 +61,7 @@ export function useExhibitionSheet(data, config = {}) {
|
|
|
61
61
|
{ key: 'originalOwner', label: 'sheet.field.originalOwner', value: 'initial_owner' },
|
|
62
62
|
{ key: 'currentOwner', label: 'sheet.field.currentOwner', value: 'owner' },
|
|
63
63
|
{ key: 'date', label: 'sheet.field.date', value: 'dates' },
|
|
64
|
-
{ key: 'artist', label: 'sheet.field.artists', value: (c) => artistLine((c.record
|
|
64
|
+
{ key: 'artist', label: 'sheet.field.artists', value: (c) => artistLine(recordArtist(c.record, c.text), c.text, { style: 'line' }) },
|
|
65
65
|
{ key: 'scribe', label: 'sheet.field.scribe', value: 'scriber' },
|
|
66
66
|
{ key: 'workshop', label: 'sheet.field.workshop', value: 'workshop' },
|
|
67
67
|
{ key: 'type', label: 'sheet.field.type', value: 'type' },
|
|
@@ -87,13 +87,8 @@ export function useExhibitionSheet(data, config = {}) {
|
|
|
87
87
|
const itemSheet = {
|
|
88
88
|
entity: 'items',
|
|
89
89
|
translations: ['glossary', 'dynasties', 'partners'],
|
|
90
|
-
//
|
|
91
|
-
//
|
|
92
|
-
// known gap, recorded in the exporter's README). Legacy printed them on
|
|
93
|
-
// every sheet, so the platform reads them off another row when the
|
|
94
|
-
// active one has neither — the one place a page reads across
|
|
95
|
-
// languages, and only for proper names.
|
|
96
|
-
attribution: ['author', 'copy_editor'],
|
|
90
|
+
// Credits are the sheet language's own: dxa-api read them from that
|
|
91
|
+
// language's authors_objects rows and printed nothing when it had none.
|
|
97
92
|
fields,
|
|
98
93
|
layout: 'list',
|
|
99
94
|
shortDescription: 'short_description',
|
|
@@ -104,9 +99,8 @@ export function useExhibitionSheet(data, config = {}) {
|
|
|
104
99
|
.map((p) => ({
|
|
105
100
|
url: p.url,
|
|
106
101
|
alt: labelOf('items', record.id),
|
|
107
|
-
caption: p.captions?.[language] ??
|
|
108
|
-
|
|
109
|
-
copyright: p.copyright ?? '',
|
|
102
|
+
caption: p.captions?.[language] ?? '',
|
|
103
|
+
...pictureCredits(p, language),
|
|
110
104
|
})),
|
|
111
105
|
citation: {
|
|
112
106
|
permalink: false,
|
|
@@ -121,11 +115,13 @@ export function useExhibitionSheet(data, config = {}) {
|
|
|
121
115
|
variant: 'grid',
|
|
122
116
|
heading: 'exhibition.related.objects',
|
|
123
117
|
actionLabel: 'catalogue.results.seeDatabaseEntry',
|
|
124
|
-
|
|
118
|
+
// In the sheet's language when the related record has it, in the main
|
|
119
|
+
// language otherwise, as legacy's relatedObjectsMainLanguage did.
|
|
120
|
+
record: ({ record: other, justification }, { language = defaultLang } = {}) => ({
|
|
125
121
|
id: other.id,
|
|
126
122
|
image: other.images?.[0]?.url ?? '',
|
|
127
123
|
imageAlt: labelOf('items', other.id),
|
|
128
|
-
name: mdInline(tr('items', other.id, defaultLang).name ?? other.internal_name ?? ''),
|
|
124
|
+
name: mdInline(translations('items', language)[other.id]?.name ?? tr('items', other.id, defaultLang).name ?? other.internal_name ?? ''),
|
|
129
125
|
meta: [labelOf('countries', other.country_id), justification ? mdStrip(justification) : ''].filter(Boolean),
|
|
130
126
|
to: itemRoute(other),
|
|
131
127
|
}),
|
|
@@ -2,6 +2,7 @@ import { computed } from 'vue'
|
|
|
2
2
|
import { useI18n } from '../../i18n/index.js'
|
|
3
3
|
import { entityRef } from '../../composables/useEntities.js'
|
|
4
4
|
import { useCollectionTree } from '../../record/collectionTree.js'
|
|
5
|
+
import { recordArtist } from '../../record/derivations.js'
|
|
5
6
|
|
|
6
7
|
// An exhibition's themes: the tour its theme pages, its themes list and its
|
|
7
8
|
// theme galleries read (inventory-app#2053). The six exhibitions each carried
|
|
@@ -240,7 +241,8 @@ export function useExhibitionThemes(data) {
|
|
|
240
241
|
if (sheet.alternate_name) {
|
|
241
242
|
fields.push({ label: `${t('sheet.field.alsoKnownAs')}:`, value: mdInline(sheet.alternate_name) })
|
|
242
243
|
}
|
|
243
|
-
|
|
244
|
+
const artist = recordArtist(parent, sheet)
|
|
245
|
+
if (artist) fields.push({ label: '', value: artist })
|
|
244
246
|
if (sheet.dates) fields.push({ label: '', value: sheet.dates })
|
|
245
247
|
return fields
|
|
246
248
|
}
|
|
@@ -25,13 +25,13 @@ import { useGallerySheet } from './useGallerySheet.js'
|
|
|
25
25
|
* The gallery's item-page spec, over its data layer's `data` and its
|
|
26
26
|
* timeline layer's `timeline` (`useGalleryTimeline`).
|
|
27
27
|
*
|
|
28
|
-
* Reads `data.
|
|
28
|
+
* Reads `data.dynastyById`, `data.labelOf`,
|
|
29
29
|
* `data.partnerById`, `data.partnerRoute`, `data.translations`, what
|
|
30
30
|
* `useGallerySheet` reads, and `timeline.countryIdForCode`,
|
|
31
31
|
* `timeline.timelineEvents` (`countries`, `findEvents`).
|
|
32
32
|
*/
|
|
33
33
|
export function useGalleryItemDetail(data, timeline) {
|
|
34
|
-
const {
|
|
34
|
+
const { dynastyById, labelOf, partnerById, partnerRoute, translations } = data
|
|
35
35
|
const { countryIdForCode, timelineEvents } = timeline
|
|
36
36
|
const { countries: timelineCountries, findEvents } = timelineEvents
|
|
37
37
|
const { itemSheet: platformSheet } = useGallerySheet(data)
|
|
@@ -43,8 +43,9 @@ export function useGalleryItemDetail(data, timeline) {
|
|
|
43
43
|
return partner ? partnerRoute(partner) : null
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
// The sheet language's own: dxa-api returned a dynasty only in it.
|
|
46
47
|
function dynastyTranslation(dynasty, language) {
|
|
47
|
-
return translations('dynasties', language)[dynasty.id] ??
|
|
48
|
+
return translations('dynasties', language)[dynasty.id] ?? {}
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
// Legacy popped out a dynasty only when it had a history to show.
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
// dynasties stand in when it has none, and keep their own popouts
|
|
28
28
|
// (decision D5, inventory-app#2233).
|
|
29
29
|
|
|
30
|
-
import { artistLine } from '../../record/derivations.js'
|
|
30
|
+
import { artistLine, pictureCredits, recordArtist } from '../../record/derivations.js'
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
33
|
* The gallery's item-sheet spec, over its data layer's `data`.
|
|
@@ -44,7 +44,7 @@ export function useGallerySheet(data, config = {}) {
|
|
|
44
44
|
|
|
45
45
|
const dynastyNames = (c) =>
|
|
46
46
|
(c.record.dynasty_ids ?? [])
|
|
47
|
-
.map((id) => translations('dynasties', c.language)[id]?.name ??
|
|
47
|
+
.map((id) => translations('dynasties', c.language)[id]?.name ?? '')
|
|
48
48
|
.filter(Boolean)
|
|
49
49
|
.join(', ')
|
|
50
50
|
|
|
@@ -61,7 +61,7 @@ export function useGallerySheet(data, config = {}) {
|
|
|
61
61
|
{ key: 'originalOwner', label: 'sheet.field.originalOwner', value: 'initial_owner' },
|
|
62
62
|
{ key: 'currentOwner', label: 'sheet.field.currentOwner', value: 'owner' },
|
|
63
63
|
{ key: 'date', label: 'sheet.field.date', value: 'dates' },
|
|
64
|
-
{ key: 'artist', label: 'sheet.field.artists', value: (c) => artistLine((c.record
|
|
64
|
+
{ key: 'artist', label: 'sheet.field.artists', value: (c) => artistLine(recordArtist(c.record, c.text), c.text, { style: 'line' }) },
|
|
65
65
|
{ key: 'scribe', label: 'sheet.field.scribe', value: 'scriber' },
|
|
66
66
|
{ key: 'workshop', label: 'sheet.field.workshop', value: 'workshop' },
|
|
67
67
|
{ key: 'type', label: 'sheet.field.type', value: 'type' },
|
|
@@ -87,13 +87,8 @@ export function useGallerySheet(data, config = {}) {
|
|
|
87
87
|
const itemSheet = {
|
|
88
88
|
entity: 'items',
|
|
89
89
|
translations: ['glossary', 'dynasties', 'partners'],
|
|
90
|
-
//
|
|
91
|
-
//
|
|
92
|
-
// known gap, recorded in the exporter's README). Legacy printed them on
|
|
93
|
-
// every sheet, so the platform reads them off another row when the
|
|
94
|
-
// active one has neither — the one place a page reads across
|
|
95
|
-
// languages, and only for proper names.
|
|
96
|
-
attribution: ['author', 'copy_editor'],
|
|
90
|
+
// Credits are the sheet language's own: dxa-api read them from that
|
|
91
|
+
// language's authors_objects rows and printed nothing when it had none.
|
|
97
92
|
fields,
|
|
98
93
|
layout: 'list',
|
|
99
94
|
shortDescription: 'short_description',
|
|
@@ -104,20 +99,21 @@ export function useGallerySheet(data, config = {}) {
|
|
|
104
99
|
.map((p) => ({
|
|
105
100
|
url: p.url,
|
|
106
101
|
alt: labelOf('items', record.id),
|
|
107
|
-
caption: p.captions?.[language] ??
|
|
108
|
-
|
|
109
|
-
copyright: p.copyright ?? '',
|
|
102
|
+
caption: p.captions?.[language] ?? '',
|
|
103
|
+
...pictureCredits(p, language),
|
|
110
104
|
})),
|
|
111
105
|
citation: { permalink: false, heading: 'record.citation.ofThisPage' },
|
|
112
106
|
related: {
|
|
113
107
|
variant: 'grid',
|
|
114
108
|
heading: 'record.related.items',
|
|
115
109
|
actionLabel: 'catalogue.results.seeDatabaseEntry',
|
|
116
|
-
|
|
110
|
+
// In the sheet's language when the related record has it, in the main
|
|
111
|
+
// language otherwise, as legacy's relatedObjectsMainLanguage did.
|
|
112
|
+
record: ({ record: other, justification }, { language = defaultLang } = {}) => ({
|
|
117
113
|
id: other.id,
|
|
118
114
|
image: other.images?.[0]?.url ?? '',
|
|
119
115
|
imageAlt: labelOf('items', other.id),
|
|
120
|
-
name: mdInline(tr('items', other.id, defaultLang).name ?? other.internal_name ?? ''),
|
|
116
|
+
name: mdInline(translations('items', language)[other.id]?.name ?? tr('items', other.id, defaultLang).name ?? other.internal_name ?? ''),
|
|
121
117
|
meta: [labelOf('countries', other.country_id), justification ? mdStrip(justification) : ''].filter(Boolean),
|
|
122
118
|
to: itemRoute(other),
|
|
123
119
|
}),
|
package/src/index.js
CHANGED
|
@@ -39,7 +39,7 @@ export {
|
|
|
39
39
|
glossaryEntries, glossaryTermsFor, glossaryTermsForText, sheetRows, useRecordSheet,
|
|
40
40
|
} from './record/recordSheet.js'
|
|
41
41
|
export {
|
|
42
|
-
artistLine, citation, relatedRecords, removeEndingPoint, searchGlossary, timelineLinkFor, useGlossaryPopup,
|
|
42
|
+
artistLine, citation, pictureCredits, recordArtist, relatedRecords, removeEndingPoint, searchGlossary, timelineLinkFor, useGlossaryPopup,
|
|
43
43
|
useRelatedRecords,
|
|
44
44
|
} from './record/derivations.js'
|
|
45
45
|
export { buildCollectionTree, collectionTreeFromThemes, useCollectionTree } from './record/collectionTree.js'
|
|
@@ -43,8 +43,9 @@ export function useGlossaryPopup(entries) {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
/**
|
|
46
|
-
* The glossary terms whose spelling starts with `input`,
|
|
47
|
-
*
|
|
46
|
+
* The glossary terms whose spelling starts with `input`, among those that have
|
|
47
|
+
* a row in `language` (legacy searched that language's glossary only), for a
|
|
48
|
+
* glossary search box: `[{ id, word,
|
|
48
49
|
* spelling, definition }]`, one per term, sorted by spelling.
|
|
49
50
|
*/
|
|
50
51
|
export function searchGlossary(input, language, { entity = 'glossary' } = {}) {
|
|
@@ -52,10 +53,10 @@ export function searchGlossary(input, language, { entity = 'glossary' } = {}) {
|
|
|
52
53
|
if (!needle) return []
|
|
53
54
|
const { translations } = useDataPackage()
|
|
54
55
|
const rows = translations(entity, language)
|
|
55
|
-
const fallback = translations(entity, BASE_LANGUAGE)
|
|
56
56
|
const out = []
|
|
57
57
|
for (const term of entityRef(entity).value ?? []) {
|
|
58
|
-
const t = rows[term.id]
|
|
58
|
+
const t = rows[term.id]
|
|
59
|
+
if (!t) continue
|
|
59
60
|
const spellings = t.spellings?.length ? t.spellings : [term.word]
|
|
60
61
|
for (const spelling of spellings) {
|
|
61
62
|
if (String(spelling ?? '').toLowerCase().startsWith(needle)) {
|
|
@@ -95,7 +96,7 @@ export function citation({ author, name, project, publisher = 'Museum With No Fr
|
|
|
95
96
|
/**
|
|
96
97
|
* A record's `related_items`, split by whether the package carries them:
|
|
97
98
|
* `inPackage` is `[{ reference, record, justification }]` with the
|
|
98
|
-
* justification in `language`
|
|
99
|
+
* justification in `language` only, as legacy printed it; `outside` is the
|
|
99
100
|
* references the package does not hold, kept as the references they are — a
|
|
100
101
|
* link is never dropped for being unresolvable, and never invented either.
|
|
101
102
|
*/
|
|
@@ -116,7 +117,7 @@ export function relatedRecords(record, { entity = 'items', language = BASE_LANGU
|
|
|
116
117
|
inPackage.push({
|
|
117
118
|
reference,
|
|
118
119
|
record: found,
|
|
119
|
-
justification: justifications[language] ??
|
|
120
|
+
justification: justifications[language] ?? '',
|
|
120
121
|
})
|
|
121
122
|
}
|
|
122
123
|
return { inPackage, outside }
|
|
@@ -179,8 +180,31 @@ export function removeEndingPoint(text) {
|
|
|
179
180
|
return body.slice(0, start)
|
|
180
181
|
}
|
|
181
182
|
|
|
183
|
+
/**
|
|
184
|
+
* A picture's photographer and copyright in `language`. Legacy kept them per
|
|
185
|
+
* language, like the caption, and printed only the page language's. A package
|
|
186
|
+
* exported before the per-language `photographers` and `copyrights` maps
|
|
187
|
+
* existed has only the flat fields, which it reads instead.
|
|
188
|
+
*/
|
|
189
|
+
export function pictureCredits(picture, language) {
|
|
190
|
+
if (picture?.photographers || picture?.copyrights) {
|
|
191
|
+
return { photographer: picture.photographers?.[language] ?? '', copyright: picture.copyrights?.[language] ?? '' }
|
|
192
|
+
}
|
|
193
|
+
return { photographer: picture?.photographer ?? '', copyright: picture?.copyright ?? '' }
|
|
194
|
+
}
|
|
195
|
+
|
|
182
196
|
const lifePart = (value) => (typeof value === 'string' ? value.trim() : '')
|
|
183
197
|
|
|
198
|
+
/**
|
|
199
|
+
* The artist the sheet names: the translation's own `artist` when it has one,
|
|
200
|
+
* else the record's `artist_names`. A Sharing History record has no artist
|
|
201
|
+
* entity, and legacy wrote its artist per language ("Unknown", "Inconnu"),
|
|
202
|
+
* so the package carries it on each translation (inventory-app#2233, C22).
|
|
203
|
+
*/
|
|
204
|
+
export function recordArtist(record, text) {
|
|
205
|
+
return lifePart(text?.artist) || (record?.artist_names ?? []).join(', ')
|
|
206
|
+
}
|
|
207
|
+
|
|
184
208
|
/**
|
|
185
209
|
* The artist line as Markdown: `artist`, then the artist's life data the
|
|
186
210
|
* record's translation carries (`artist_birthdate`, `artist_birthplace`,
|
|
@@ -14,15 +14,15 @@ const BASE_LANGUAGE = 'en'
|
|
|
14
14
|
// Seven pages wrote that choreography by hand. This is the one place it is
|
|
15
15
|
// written, on top of `useRecordLanguage`, which decides the language.
|
|
16
16
|
|
|
17
|
-
// A term's row in one language: its spellings and definition from
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
// all. Shared by `glossaryTermsFor` (walks a record's own `glossary_ids`)
|
|
17
|
+
// A term's row in one language: its spellings and definition from `rows`,
|
|
18
|
+
// its headword from `term` (the glossary record itself, language-independent)
|
|
19
|
+
// — `null` when the term has no row in that language. Legacy linked a term
|
|
20
|
+
// only in a language its glossary had it in, never through another. Shared by `glossaryTermsFor` (walks a record's own `glossary_ids`)
|
|
22
21
|
// and `glossaryTermsForText` (walks the whole glossary, filtered by what
|
|
23
22
|
// the text contains), so the two agree on what a term row looks like.
|
|
24
|
-
function termRow(id, term, rows
|
|
25
|
-
const t = rows[id]
|
|
23
|
+
function termRow(id, term, rows) {
|
|
24
|
+
const t = rows[id]
|
|
25
|
+
if (!t) return null
|
|
26
26
|
const spellings = (t.spellings ?? []).map((s) => String(s).trim()).filter(Boolean)
|
|
27
27
|
const word = term?.word ?? spellings[0] ?? ''
|
|
28
28
|
if (!word && spellings.length === 0) return null
|
|
@@ -31,17 +31,16 @@ function termRow(id, term, rows, fallback) {
|
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
33
|
* `{ id, word, definition, spellings }` for each glossary term of `record`
|
|
34
|
-
* (its `glossary_ids`)
|
|
35
|
-
*
|
|
34
|
+
* (its `glossary_ids`) that has a row in `language`, with its spellings and
|
|
35
|
+
* definition in it, its headword standing in for a row without spellings.
|
|
36
36
|
*/
|
|
37
37
|
export function glossaryTermsFor(record, language, { entity = 'glossary', translations, byId } = {}) {
|
|
38
38
|
const ids = record?.glossary_ids ?? []
|
|
39
39
|
if (ids.length === 0) return []
|
|
40
40
|
const rows = translations(entity, language)
|
|
41
|
-
const fallback = translations(entity, BASE_LANGUAGE)
|
|
42
41
|
const out = []
|
|
43
42
|
for (const id of ids) {
|
|
44
|
-
const row = termRow(id, byId?.get(id), rows
|
|
43
|
+
const row = termRow(id, byId?.get(id), rows)
|
|
45
44
|
if (row) out.push(row)
|
|
46
45
|
}
|
|
47
46
|
return out
|
|
@@ -53,7 +52,7 @@ export function glossaryTermsFor(record, language, { entity = 'glossary', transl
|
|
|
53
52
|
* spelling occurs in `text`, matched the same word-bounded way `md`/
|
|
54
53
|
* `renderBlock` highlight it, so what this returns and what the text is
|
|
55
54
|
* rendered with agree. The candidate list — the whole glossary in
|
|
56
|
-
* `language
|
|
55
|
+
* `language` — is the same for every text asked about
|
|
57
56
|
* in that language; `glossaryIdsIn` is what carries the caching (see
|
|
58
57
|
* `markdown.js`), so building it here costs one pass over the glossary,
|
|
59
58
|
* not a rebuilt regex.
|
|
@@ -63,11 +62,10 @@ export function glossaryTermsForText(text, language, { entity = 'glossary' } = {
|
|
|
63
62
|
if (!value) return []
|
|
64
63
|
const { translations } = useDataPackage()
|
|
65
64
|
const rows = translations(entity, language)
|
|
66
|
-
const fallback = translations(entity, BASE_LANGUAGE)
|
|
67
65
|
const byId = new Map()
|
|
68
66
|
const candidates = []
|
|
69
67
|
for (const term of entityRef(entity).value ?? []) {
|
|
70
|
-
const row = termRow(term.id, term, rows
|
|
68
|
+
const row = termRow(term.id, term, rows)
|
|
71
69
|
if (!row) continue
|
|
72
70
|
byId.set(term.id, row)
|
|
73
71
|
for (const spelling of row.spellings) candidates.push({ id: term.id, spelling })
|
|
@@ -98,9 +96,9 @@ export function glossaryEntries(terms) {
|
|
|
98
96
|
*
|
|
99
97
|
* `attribution: ['author', 'copy_editor']` names fields that are proper
|
|
100
98
|
* names rather than text: when the active language's row lacks them, the
|
|
101
|
-
* first other language of the record that has them supplies them.
|
|
102
|
-
*
|
|
103
|
-
*
|
|
99
|
+
* first other language of the record that has them supplies them. No
|
|
100
|
+
* platform sheet asks for it any more: legacy printed a language's own
|
|
101
|
+
* credits only, and nothing where it had none.
|
|
104
102
|
*/
|
|
105
103
|
export function useRecordSheet(record, { entity, translations: related = [], attribution: attributionFields = [], languages } = {}) {
|
|
106
104
|
if (!entity) throw new Error('useRecordSheet: entity is required')
|
|
@@ -143,8 +141,9 @@ export function useRecordSheet(record, { entity, translations: related = [], att
|
|
|
143
141
|
return
|
|
144
142
|
}
|
|
145
143
|
// The record language and English both: `tr` falls back to English for a
|
|
146
|
-
// record
|
|
147
|
-
// that has not been loaded is a blank, not a fallback.
|
|
144
|
+
// record that has no row in the active language, as legacy's PHP sheets
|
|
145
|
+
// did, and a fallback that has not been loaded is a blank, not a fallback.
|
|
146
|
+
// Glossary terms, dynasties and the rest stay in the active language.
|
|
148
147
|
const languages = lang === BASE_LANGUAGE ? [lang] : [lang, BASE_LANGUAGE]
|
|
149
148
|
await Promise.all(
|
|
150
149
|
[entity, ...related].flatMap((name) => languages.map((code) => pkg.loadTranslations(name, code))),
|