@museumwnf/viewer-core 2.8.0 → 2.10.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@museumwnf/viewer-core",
3
- "version": "2.8.0",
3
+ "version": "2.10.0",
4
4
  "description": "Application engine for MWNF websites: turns a dataset.config.js into a mounted Vue application.",
5
5
  "type": "module",
6
6
  "license": "SEE LICENSE IN LICENSE.md",
@@ -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, pictureCredits, recordArtist } from '../../record/derivations.js'
30
+ import { artistLine, pictureCredits, picturesInLanguage, recordArtist } from '../../record/derivations.js'
31
31
 
32
32
  /**
33
33
  * The exhibition's item-sheet spec, over its data layer's `data`.
@@ -88,13 +88,17 @@ export function useExhibitionSheet(data, config = {}) {
88
88
  entity: 'items',
89
89
  translations: ['glossary', 'dynasties', 'partners'],
90
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.
91
+ // language's authors_objects rows (else the record's own credit column)
92
+ // and printed nothing when it had neither. Each person is printed as its
93
+ // SQL did, "Firstname LASTNAME" (`credit_names`, `creditLine`).
94
+ creditNames: true,
92
95
  fields,
93
96
  layout: 'list',
94
97
  shortDescription: 'short_description',
95
98
  shortDescriptionAfter: 'description',
99
+ // Only the pictures with a row in the sheet's language, as dxa-api listed them.
96
100
  media: (record, { language }) =>
97
- [...(record.images ?? [])]
101
+ [...picturesInLanguage(record.images, language)]
98
102
  .sort((a, b) => (a.display_order ?? 0) - (b.display_order ?? 0))
99
103
  .map((p) => ({
100
104
  url: p.url,
@@ -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, pictureCredits, recordArtist } from '../../record/derivations.js'
30
+ import { artistLine, pictureCredits, picturesInLanguage, recordArtist } from '../../record/derivations.js'
31
31
 
32
32
  /**
33
33
  * The gallery's item-sheet spec, over its data layer's `data`.
@@ -88,13 +88,17 @@ export function useGallerySheet(data, config = {}) {
88
88
  entity: 'items',
89
89
  translations: ['glossary', 'dynasties', 'partners'],
90
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.
91
+ // language's authors_objects rows (else the record's own credit column)
92
+ // and printed nothing when it had neither. Each person is printed as its
93
+ // SQL did, "Firstname LASTNAME" (`credit_names`, `creditLine`).
94
+ creditNames: true,
92
95
  fields,
93
96
  layout: 'list',
94
97
  shortDescription: 'short_description',
95
98
  shortDescriptionAfter: 'description',
99
+ // Only the pictures with a row in the sheet's language, as dxa-api listed them.
96
100
  media: (record, { language }) =>
97
- [...(record.images ?? [])]
101
+ [...picturesInLanguage(record.images, language)]
98
102
  .sort((a, b) => (a.display_order ?? 0) - (b.display_order ?? 0))
99
103
  .map((p) => ({
100
104
  url: p.url,
@@ -30,6 +30,27 @@ const escaping = {
30
30
  },
31
31
  }
32
32
 
33
+ // No text on these websites is code. Legacy wrote the backtick as a letter
34
+ // (the ʿayn of "Fu`ad", "`Abbas"), and a text pasted from a word processor can
35
+ // hold one anywhere; read as Markdown, two of them turned the words between
36
+ // into a code span, printed in a code font with any markup inside as
37
+ // characters. An indented line was a code block the same way. Without these
38
+ // three tokenizers a backtick is the character it is, and an indented line a
39
+ // line of its paragraph.
40
+ const noCode = {
41
+ tokenizer: {
42
+ codespan() {
43
+ return undefined
44
+ },
45
+ code() {
46
+ return undefined
47
+ },
48
+ fences() {
49
+ return undefined
50
+ },
51
+ },
52
+ }
53
+
33
54
  // `breaks` is the one difference, and it is a difference in the source rather
34
55
  // than in policy. A text in the dictionary is written as prose and wraps freely,
35
56
  // so a single newline means nothing. A record's field arrives from a database
@@ -37,8 +58,8 @@ const escaping = {
37
58
  // together.
38
59
  const prose = new Marked({ async: false, gfm: true, breaks: false })
39
60
  const record = new Marked({ async: false, gfm: true, breaks: true })
40
- prose.use(escaping)
41
- record.use(escaping)
61
+ prose.use(escaping, noCode)
62
+ record.use(escaping, noCode)
42
63
 
43
64
  const pipeline = (breaks) => (breaks ? record : prose)
44
65
 
@@ -123,8 +144,8 @@ function withGlossary(breaks, glossary) {
123
144
  } else {
124
145
  const withProse = new Marked({ async: false, gfm: true, breaks: false })
125
146
  const withRecord = new Marked({ async: false, gfm: true, breaks: true })
126
- withProse.use(escaping, { extensions: [extension] })
127
- withRecord.use(escaping, { extensions: [extension] })
147
+ withProse.use(escaping, noCode, { extensions: [extension] })
148
+ withRecord.use(escaping, noCode, { extensions: [extension] })
128
149
  entry = { prose: withProse, record: withRecord }
129
150
  }
130
151
  glossaryPipelines.set(key, entry)
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, pictureCredits, recordArtist, relatedRecords, removeEndingPoint, searchGlossary, timelineLinkFor, useGlossaryPopup,
42
+ artistLine, citation, creditLine, pictureCredits, picturesInLanguage, 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'
@@ -193,16 +193,46 @@ export function pictureCredits(picture, language) {
193
193
  return { photographer: picture?.photographer ?? '', copyright: picture?.copyright ?? '' }
194
194
  }
195
195
 
196
+ /**
197
+ * The pictures a page in `language` shows. Legacy listed a picture on a page
198
+ * only where it had a row in that language, empty or not; a package carries
199
+ * those languages per picture (`languages`). A package exported before them
200
+ * has no such list, and every picture shows, as before.
201
+ */
202
+ export function picturesInLanguage(pictures, language) {
203
+ return (pictures ?? []).filter((picture) => !Array.isArray(picture?.languages) || picture.languages.includes(language))
204
+ }
205
+
206
+ /**
207
+ * A credit line from its people (`credit_names[role]` in a package): each one
208
+ * as the galleries' sheet printed them, "Firstname LASTNAME" (dxa-api's SQL
209
+ * capitalised the surname; the data keeps it as written), or as written when
210
+ * the entry is a spelling of its own (`{ name }`: the original script on an
211
+ * Arabic page, or the record's own credit column). Joined with ", ".
212
+ */
213
+ export function creditLine(names) {
214
+ return (names ?? [])
215
+ .map((entry) => {
216
+ if (typeof entry?.name === 'string') return entry.name.trim()
217
+ return [entry?.firstname?.trim(), entry?.lastname?.trim()?.toLocaleUpperCase()].filter(Boolean).join(' ')
218
+ })
219
+ .filter(Boolean)
220
+ .join(', ')
221
+ }
222
+
196
223
  const lifePart = (value) => (typeof value === 'string' ? value.trim() : '')
197
224
 
198
225
  /**
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).
226
+ * The artist the sheet names: the translation's own `artist`. Legacy wrote the
227
+ * artist line per language ("Unknown", "Inconnu", or a sentence in Arabic) and
228
+ * printed a language's own line, or nothing when that language had none, so
229
+ * the package carries it on each translation (inventory-app#2233). The
230
+ * record's `artist_names` come from one language's line and stand for none;
231
+ * the record stays a parameter so callers need not change.
203
232
  */
233
+ // eslint-disable-next-line no-unused-vars
204
234
  export function recordArtist(record, text) {
205
- return lifePart(text?.artist) || (record?.artist_names ?? []).join(', ')
235
+ return lifePart(text?.artist)
206
236
  }
207
237
 
208
238
  /**