@museumwnf/viewer-core 1.14.0 → 1.15.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/LICENSE.md +44 -44
- package/README.md +658 -635
- package/package.json +50 -50
- package/src/AppRoot.vue +58 -58
- package/src/catalogue/dates.js +209 -209
- package/src/catalogue/facets.js +61 -61
- package/src/catalogue/keywordIndex.js +289 -289
- package/src/catalogue/listQuery.js +100 -100
- package/src/catalogue/pagination.js +57 -57
- package/src/catalogue/searchLanguage.js +32 -32
- package/src/catalogue/timeline.js +220 -220
- package/src/composables/useCatalogueData.js +141 -141
- package/src/composables/useDataPackage.js +98 -98
- package/src/composables/useEntities.js +100 -100
- package/src/composables/useRecordLanguage.js +71 -71
- package/src/conventions.js +252 -148
- package/src/createViewer.js +29 -29
- package/src/i18n/I18nText.vue +17 -17
- package/src/i18n/I18nTextInline.vue +17 -17
- package/src/i18n/index.js +88 -88
- package/src/i18n/language.js +127 -127
- package/src/i18n/markdown.js +265 -265
- package/src/index.js +51 -50
- package/src/injectionKeys.js +1 -1
- package/src/languages.js +49 -49
- package/src/legacy/index.js +58 -58
- package/src/record/collectionTree.js +307 -307
- package/src/record/derivations.js +154 -154
- package/src/record/partners.js +87 -87
- package/src/record/recordSheet.js +231 -231
- package/src/rights.js +53 -53
- package/src/router/index.js +138 -138
- package/src/siteConfig.js +37 -37
- package/src/styles/base.css +49 -49
- package/src/testing/checkOfferedLanguages.js +67 -67
- package/src/testing/index.js +3 -3
- package/src/testing/smoke.js +103 -103
- package/src/testing/viteConfig.js +90 -90
- package/src/views/DetailView.vue +53 -53
- package/src/views/HomeView.vue +20 -20
- package/src/views/ListView.vue +61 -61
- package/src/views/NotFoundView.vue +8 -8
- package/src/vite.js +8 -8
package/src/conventions.js
CHANGED
|
@@ -1,148 +1,252 @@
|
|
|
1
|
-
import { computed, toValue } from 'vue'
|
|
2
|
-
import { useRoute } from 'vue-router'
|
|
3
|
-
import { useI18n } from './i18n/index.js'
|
|
4
|
-
import { entityRef } from './composables/useEntities.js'
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
//
|
|
9
|
-
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
//
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
*
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
*
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
1
|
+
import { computed, toValue } from 'vue'
|
|
2
|
+
import { useRoute } from 'vue-router'
|
|
3
|
+
import { useI18n } from './i18n/index.js'
|
|
4
|
+
import { entityRef } from './composables/useEntities.js'
|
|
5
|
+
import { useDataPackage } from './composables/useDataPackage.js'
|
|
6
|
+
import { resolveRecordLanguage } from './composables/useRecordLanguage.js'
|
|
7
|
+
|
|
8
|
+
// Three small conventions each website had written for itself.
|
|
9
|
+
|
|
10
|
+
// ── Project names ──────────────────────────────────────────────────────────
|
|
11
|
+
//
|
|
12
|
+
// `mwnf3.projectnames`, the whole table rather than the projects a website
|
|
13
|
+
// happens to borrow from, since that set moves with every reimport. The key
|
|
14
|
+
// on the left is data; the entry on the right is a shared text
|
|
15
|
+
// (`core.project.*` in viewer-i18n), written out so a reader can see the
|
|
16
|
+
// eight this asks for. A key that is not here reads as itself.
|
|
17
|
+
//
|
|
18
|
+
// @deprecated Project data belongs in the data package, not in viewer-core.
|
|
19
|
+
// Read a project's name from `manifest.projects` via `projectLabel()` /
|
|
20
|
+
// `useProjects()` instead (epic metanull/inventory-app#1727, phase 3) — see
|
|
21
|
+
// the migration table below. Kept only for sites that have not migrated yet
|
|
22
|
+
// (phase 4); removed in the cleanup wave.
|
|
23
|
+
export const PROJECT_ENTRIES = Object.freeze({
|
|
24
|
+
ISL: 'core.project.islamicArt',
|
|
25
|
+
EPM: 'core.project.explorePartners',
|
|
26
|
+
DBA: 'core.project.baroqueArt',
|
|
27
|
+
BAR: 'core.project.baroqueArt',
|
|
28
|
+
AWE: 'core.project.sharingHistory',
|
|
29
|
+
awe: 'core.project.sharingHistory',
|
|
30
|
+
DCA: 'core.project.carpetArt',
|
|
31
|
+
DGA: 'core.project.glassArt',
|
|
32
|
+
EXTHE: 'core.project.tableIsSet',
|
|
33
|
+
GALLERIES: 'core.project.galleries',
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
/** The name of a project by its legacy key, through `t`; the key itself when unknown. */
|
|
37
|
+
export function projectName(key, t) {
|
|
38
|
+
const entry = PROJECT_ENTRIES[key]
|
|
39
|
+
return entry ? t(entry) : (key ?? '')
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** `projectName` bound to the installed texts, for a component. */
|
|
43
|
+
export function useProjectName() {
|
|
44
|
+
const { t } = useI18n()
|
|
45
|
+
return (key) => projectName(key, t)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// A member is borrowed from the MWNF project that originally published it,
|
|
49
|
+
// and legacy names and colours that project on the item sheet and in the
|
|
50
|
+
// results grid — two separate mappings, because legacy keeps them separate:
|
|
51
|
+
// the name is per project key (`PROJECT_ENTRIES` above), the colour is per
|
|
52
|
+
// project *family*, so ISL and EPM share a swatch and every exhibition
|
|
53
|
+
// shares another. The two exhibition sites re-hard-coded this table as
|
|
54
|
+
// English literals next to their own copy of the name table, which is why a
|
|
55
|
+
// project name translates on the galleries and not on the exhibitions.
|
|
56
|
+
//
|
|
57
|
+
// @deprecated Chip colour is a per-project site-config map keyed by the
|
|
58
|
+
// project UUID (epic #1727 decision 1: no family/parent relation is modeled
|
|
59
|
+
// anywhere, this table was always a frontend invention). Removed in the
|
|
60
|
+
// cleanup wave alongside `PROJECT_ENTRIES`.
|
|
61
|
+
export const PROJECT_FAMILIES = Object.freeze({
|
|
62
|
+
ISL: 'ISLandEPM',
|
|
63
|
+
EPM: 'ISLandEPM',
|
|
64
|
+
DBA: 'DBA',
|
|
65
|
+
BAR: 'DBA',
|
|
66
|
+
AWE: 'AWE',
|
|
67
|
+
awe: 'AWE',
|
|
68
|
+
DCA: 'DCA',
|
|
69
|
+
DGA: 'DGA',
|
|
70
|
+
EXTHE: 'EXH',
|
|
71
|
+
GALLERIES: 'Galleries',
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* A project's family by its legacy key — legacy's own class names, so a
|
|
76
|
+
* site's CSS reads as the stylesheet it was copied from. A key with no
|
|
77
|
+
* entry falls back to itself, the same rule `projectName` applies.
|
|
78
|
+
*
|
|
79
|
+
* @deprecated See `PROJECT_FAMILIES`.
|
|
80
|
+
*/
|
|
81
|
+
export function projectFamily(key) {
|
|
82
|
+
return PROJECT_FAMILIES[key] ?? (key ?? '')
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ── Projects, from the data package ─────────────────────────────────────────
|
|
86
|
+
//
|
|
87
|
+
// `manifest.projects` (epic metanull/inventory-app#1727, phase 2 —
|
|
88
|
+
// `scripts/exporters`) is what `PROJECT_ENTRIES`/`PROJECT_FAMILIES` above
|
|
89
|
+
// should have been: project data belongs in the data package a project's own
|
|
90
|
+
// exporter writes, not in a table viewer-core carries for every website. Its
|
|
91
|
+
// shape, one entry per project UUID:
|
|
92
|
+
//
|
|
93
|
+
// manifest.projects[projectId] = {
|
|
94
|
+
// name: { <lang>: '…' }, // per-language title
|
|
95
|
+
// site_url: 'https://…' | null,
|
|
96
|
+
// related_database_url: 'https://…' | null,
|
|
97
|
+
// artistic_introduction_url: 'https://…' | null,
|
|
98
|
+
// }
|
|
99
|
+
//
|
|
100
|
+
// A data package built before this phase simply has no `projects` key at
|
|
101
|
+
// all; `projectLabel`/`projectLinks` answer `null` for that case exactly as
|
|
102
|
+
// they do for an unknown `projectId` — a site notices nothing until it
|
|
103
|
+
// migrates. Migration mapping, from the epic's design comment (§B):
|
|
104
|
+
//
|
|
105
|
+
// RecordView citation line → projectLabel(manifest, record.project_id, lang) (viewer-layout#81)
|
|
106
|
+
// ItemSheet "Source database" line → same, plus a site-config colour map keyed by project UUID (no more `projectFamily`)
|
|
107
|
+
// "Search related database" block → projectLinks(manifest, id).relatedDatabaseUrl, rendered iff non-null
|
|
108
|
+
// Artistic Introduction link → projectLinks(manifest, id).artisticIntroductionUrl, iff non-null
|
|
109
|
+
// search-scope include-EPM checkbox → stays the site's own dataset.config.js scope list (not modeled here — decision 5)
|
|
110
|
+
// partner Museums/Institutions split → partner.project_uuids matched against a site-config list (decision 5)
|
|
111
|
+
//
|
|
112
|
+
// `PROJECT_ENTRIES`, `PROJECT_FAMILIES` and `projectFamily` are deprecated by
|
|
113
|
+
// this section and removed in the cleanup wave once every site has moved
|
|
114
|
+
// (phase 4). `projectName()`/`useProjectName()` are unaffected — they still
|
|
115
|
+
// resolve a *legacy key* through the installed texts, for whatever has not
|
|
116
|
+
// migrated yet.
|
|
117
|
+
|
|
118
|
+
/** One entry of `manifest.projects`, or `null` when the package predates
|
|
119
|
+
* the section or does not carry `projectId`. */
|
|
120
|
+
function projectEntry(manifest, projectId) {
|
|
121
|
+
return manifest?.projects?.[projectId] ?? null
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* The name of a project by its data-package UUID, from
|
|
126
|
+
* `manifest.projects[projectId].name`. Language fallback follows the same
|
|
127
|
+
* rule every translated field in this package does (`resolveRecordLanguage`):
|
|
128
|
+
* `lang` when the entry carries it, this package's base language (`en`)
|
|
129
|
+
* when it does not, the first language the entry carries when it has
|
|
130
|
+
* neither. `null` — never a thrown error — when the package has no
|
|
131
|
+
* `projects` section, `projectId` is not one of its keys, or the entry
|
|
132
|
+
* names nothing at all.
|
|
133
|
+
*/
|
|
134
|
+
export function projectLabel(manifest, projectId, lang) {
|
|
135
|
+
const names = projectEntry(manifest, projectId)?.name
|
|
136
|
+
const codes = names ? Object.keys(names) : []
|
|
137
|
+
if (codes.length === 0) return null
|
|
138
|
+
return names[resolveRecordLanguage(codes, lang)] ?? null
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* The three URLs `manifest.projects[projectId]` carries. `null` for the
|
|
143
|
+
* whole result (not per field) when the package predates the section or
|
|
144
|
+
* does not carry `projectId`; each field is `null` on its own when the
|
|
145
|
+
* project simply has no value for it (import time never invents one).
|
|
146
|
+
*/
|
|
147
|
+
export function projectLinks(manifest, projectId) {
|
|
148
|
+
const entry = projectEntry(manifest, projectId)
|
|
149
|
+
if (!entry) return null
|
|
150
|
+
return {
|
|
151
|
+
siteUrl: entry.site_url ?? null,
|
|
152
|
+
relatedDatabaseUrl: entry.related_database_url ?? null,
|
|
153
|
+
artisticIntroductionUrl: entry.artistic_introduction_url ?? null,
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* `projectLabel`/`projectLinks` bound to the installed data package and
|
|
159
|
+
* texts, for a component — the manifest-driven analogue of
|
|
160
|
+
* `useProjectName()`. `label(projectId)` reads the site's active language;
|
|
161
|
+
* pass a language explicitly to read another.
|
|
162
|
+
*/
|
|
163
|
+
export function useProjects() {
|
|
164
|
+
const { manifest } = useDataPackage()
|
|
165
|
+
const { locale } = useI18n()
|
|
166
|
+
return {
|
|
167
|
+
label: (projectId, lang = locale.value) => projectLabel(manifest, projectId, lang),
|
|
168
|
+
links: (projectId) => projectLinks(manifest, projectId),
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// ── The section a route belongs to ─────────────────────────────────────────
|
|
173
|
+
//
|
|
174
|
+
// A route says what section it is (`meta: { section: 'collection' }`), and
|
|
175
|
+
// the shell reads it here for the banner title or the active menu entry.
|
|
176
|
+
// Four shells derived the same thing from `route.path.startsWith(…)` chains
|
|
177
|
+
// of nine branches each; a route already knows.
|
|
178
|
+
export function useSection() {
|
|
179
|
+
const route = useRoute()
|
|
180
|
+
return computed(() => String(route.meta?.section ?? ''))
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// ── A featured record ──────────────────────────────────────────────────────
|
|
184
|
+
|
|
185
|
+
// mulberry32 — a small seedable generator, so a test can pin the pick.
|
|
186
|
+
function seeded(seed) {
|
|
187
|
+
let a = seed >>> 0
|
|
188
|
+
return () => {
|
|
189
|
+
a = (a + 0x6d2b79f5) >>> 0
|
|
190
|
+
let t = a
|
|
191
|
+
t = Math.imul(t ^ (t >>> 15), t | 1)
|
|
192
|
+
t ^= t + Math.imul(t ^ (t >>> 7), t | 61)
|
|
193
|
+
return ((t ^ (t >>> 14)) >>> 0) / 4294967296
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* One record of `entity` chosen at random for a landing page's spotlight —
|
|
199
|
+
* among those with an image when `withImage` is set (the default), among
|
|
200
|
+
* all when none has one. Null until the entity is loaded. The pick is made
|
|
201
|
+
* once per visit and again only when the records change; `seed` makes it
|
|
202
|
+
* deterministic.
|
|
203
|
+
*/
|
|
204
|
+
export function useFeaturedRecord(entity, { withImage = true, seed, images = 'images' } = {}) {
|
|
205
|
+
const records = entityRef(entity)
|
|
206
|
+
const random = seed == null ? Math.random : seeded(seed)
|
|
207
|
+
return computed(() => {
|
|
208
|
+
const all = toValue(records) ?? []
|
|
209
|
+
if (all.length === 0) return null
|
|
210
|
+
const pool = withImage ? all.filter((record) => (record?.[images]?.length ?? 0) > 0) : all
|
|
211
|
+
const candidates = pool.length > 0 ? pool : all
|
|
212
|
+
return candidates[Math.floor(random() * candidates.length)] ?? null
|
|
213
|
+
})
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// ── A route's `meta.entities` ────────────────────────────────────────────
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* `sectionMeta(chrome)` returns a `meta(section, ...entities)` helper: a
|
|
220
|
+
* route's `meta: { section, entities: [...chrome, ...entities] }` in one
|
|
221
|
+
* call. `chrome` is the entities every page of the site loads regardless of
|
|
222
|
+
* what it is (the DXA sites' `['gallery', 'items', 'partners', 'countries']`
|
|
223
|
+
* — the shell reads them on every route); a route names on top of that only
|
|
224
|
+
* the entities its own page adds. Four DXA configs each wrote this same
|
|
225
|
+
* four-line closure next to their own `chrome` list.
|
|
226
|
+
*/
|
|
227
|
+
export function sectionMeta(chrome = []) {
|
|
228
|
+
return (section, ...entities) => ({ section, entities: [...chrome, ...entities] })
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// ── The MWNF portal ─────────────────────────────────────────────────────
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* The addresses every DXA `dataset.config.js` repeats under `links` —
|
|
235
|
+
* the portal and the sibling standalone sites a gallery or an exhibition
|
|
236
|
+
* links out to. Frozen: a site's own `links` may add to this object but
|
|
237
|
+
* copying it is what this replaces.
|
|
238
|
+
*/
|
|
239
|
+
export const mwnfLinks = Object.freeze({
|
|
240
|
+
portal: 'https://www.museumwnf.org',
|
|
241
|
+
galleries: 'https://galleries.museumwnf.org',
|
|
242
|
+
myCollection: 'https://www.museumwnf.org/mycollection/index.php',
|
|
243
|
+
about: 'https://www.museumwnf.org/about',
|
|
244
|
+
contact: 'https://www.museumwnf.org/about/contact',
|
|
245
|
+
legalNotice: 'https://www.museumwnf.org/about/legal-notice',
|
|
246
|
+
credits: 'https://www.museumwnf.org/about/credits',
|
|
247
|
+
cookies: 'https://www.museumwnf.org/about/cookies',
|
|
248
|
+
overallDatabase: 'https://www.museumwnf.org/database_searchform.php',
|
|
249
|
+
islamicArt: 'https://islamicart.museumwnf.org',
|
|
250
|
+
baroqueArt: 'https://baroqueart.museumwnf.org',
|
|
251
|
+
sharingHistory: 'https://sharinghistory.museumwnf.org',
|
|
252
|
+
})
|
package/src/createViewer.js
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
import { createApp } from 'vue'
|
|
2
|
-
import AppRoot from './AppRoot.vue'
|
|
3
|
-
import { useDataPackage } from './composables/useDataPackage.js'
|
|
4
|
-
import { createI18n } from './i18n/index.js'
|
|
5
|
-
import { connectLanguageToRouter, resolveInitialLanguage } from './i18n/language.js'
|
|
6
|
-
import { VIEWER_CONFIG } from './injectionKeys.js'
|
|
7
|
-
import { createViewerRouter } from './router/index.js'
|
|
8
|
-
import { setSiteConfig } from './siteConfig.js'
|
|
9
|
-
import './styles/base.css'
|
|
10
|
-
|
|
11
|
-
export function createViewer(config = {}) {
|
|
12
|
-
const { languages: datasetLanguages } = useDataPackage()
|
|
13
|
-
const languages = config.languages ?? datasetLanguages
|
|
14
|
-
const messages = config.messages ?? {}
|
|
15
|
-
const resolved = { ...config, languages }
|
|
16
|
-
|
|
17
|
-
const router = createViewerRouter(config)
|
|
18
|
-
const i18n = createI18n({ messages, locale: resolveInitialLanguage(languages) })
|
|
19
|
-
|
|
20
|
-
const app = createApp(AppRoot)
|
|
21
|
-
setSiteConfig(resolved)
|
|
22
|
-
app.provide(VIEWER_CONFIG, resolved)
|
|
23
|
-
app.use(router)
|
|
24
|
-
app.use(i18n)
|
|
25
|
-
|
|
26
|
-
connectLanguageToRouter({ locale: i18n.locale, offered: languages, router })
|
|
27
|
-
|
|
28
|
-
return app
|
|
29
|
-
}
|
|
1
|
+
import { createApp } from 'vue'
|
|
2
|
+
import AppRoot from './AppRoot.vue'
|
|
3
|
+
import { useDataPackage } from './composables/useDataPackage.js'
|
|
4
|
+
import { createI18n } from './i18n/index.js'
|
|
5
|
+
import { connectLanguageToRouter, resolveInitialLanguage } from './i18n/language.js'
|
|
6
|
+
import { VIEWER_CONFIG } from './injectionKeys.js'
|
|
7
|
+
import { createViewerRouter } from './router/index.js'
|
|
8
|
+
import { setSiteConfig } from './siteConfig.js'
|
|
9
|
+
import './styles/base.css'
|
|
10
|
+
|
|
11
|
+
export function createViewer(config = {}) {
|
|
12
|
+
const { languages: datasetLanguages } = useDataPackage()
|
|
13
|
+
const languages = config.languages ?? datasetLanguages
|
|
14
|
+
const messages = config.messages ?? {}
|
|
15
|
+
const resolved = { ...config, languages }
|
|
16
|
+
|
|
17
|
+
const router = createViewerRouter(config)
|
|
18
|
+
const i18n = createI18n({ messages, locale: resolveInitialLanguage(languages) })
|
|
19
|
+
|
|
20
|
+
const app = createApp(AppRoot)
|
|
21
|
+
setSiteConfig(resolved)
|
|
22
|
+
app.provide(VIEWER_CONFIG, resolved)
|
|
23
|
+
app.use(router)
|
|
24
|
+
app.use(i18n)
|
|
25
|
+
|
|
26
|
+
connectLanguageToRouter({ locale: i18n.locale, offered: languages, router })
|
|
27
|
+
|
|
28
|
+
return app
|
|
29
|
+
}
|
package/src/i18n/I18nText.vue
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
<script setup>
|
|
2
|
-
import { computed } from 'vue'
|
|
3
|
-
import { useI18n } from './index.js'
|
|
4
|
-
import { renderBlock } from './markdown.js'
|
|
5
|
-
|
|
6
|
-
const props = defineProps({
|
|
7
|
-
keypath: { type: String, required: true },
|
|
8
|
-
tag: { type: String, default: 'div' },
|
|
9
|
-
})
|
|
10
|
-
|
|
11
|
-
const { t } = useI18n()
|
|
12
|
-
const html = computed(() => renderBlock(t(props.keypath)))
|
|
13
|
-
</script>
|
|
14
|
-
|
|
15
|
-
<template>
|
|
16
|
-
<component :is="tag" class="vc-text" v-html="html" />
|
|
17
|
-
</template>
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed } from 'vue'
|
|
3
|
+
import { useI18n } from './index.js'
|
|
4
|
+
import { renderBlock } from './markdown.js'
|
|
5
|
+
|
|
6
|
+
const props = defineProps({
|
|
7
|
+
keypath: { type: String, required: true },
|
|
8
|
+
tag: { type: String, default: 'div' },
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
const { t } = useI18n()
|
|
12
|
+
const html = computed(() => renderBlock(t(props.keypath)))
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<template>
|
|
16
|
+
<component :is="tag" class="vc-text" v-html="html" />
|
|
17
|
+
</template>
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
<script setup>
|
|
2
|
-
import { computed } from 'vue'
|
|
3
|
-
import { useI18n } from './index.js'
|
|
4
|
-
import { renderInline } from './markdown.js'
|
|
5
|
-
|
|
6
|
-
const props = defineProps({
|
|
7
|
-
keypath: { type: String, required: true },
|
|
8
|
-
tag: { type: String, default: 'span' },
|
|
9
|
-
})
|
|
10
|
-
|
|
11
|
-
const { t } = useI18n()
|
|
12
|
-
const html = computed(() => renderInline(t(props.keypath)))
|
|
13
|
-
</script>
|
|
14
|
-
|
|
15
|
-
<template>
|
|
16
|
-
<component :is="tag" class="vc-text-inline" v-html="html" />
|
|
17
|
-
</template>
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed } from 'vue'
|
|
3
|
+
import { useI18n } from './index.js'
|
|
4
|
+
import { renderInline } from './markdown.js'
|
|
5
|
+
|
|
6
|
+
const props = defineProps({
|
|
7
|
+
keypath: { type: String, required: true },
|
|
8
|
+
tag: { type: String, default: 'span' },
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
const { t } = useI18n()
|
|
12
|
+
const html = computed(() => renderInline(t(props.keypath)))
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<template>
|
|
16
|
+
<component :is="tag" class="vc-text-inline" v-html="html" />
|
|
17
|
+
</template>
|