@museumwnf/viewer-core 1.14.0 → 1.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/LICENSE.md +44 -44
  2. package/README.md +658 -635
  3. package/package.json +50 -50
  4. package/src/AppRoot.vue +58 -58
  5. package/src/catalogue/dates.js +209 -209
  6. package/src/catalogue/facets.js +61 -61
  7. package/src/catalogue/keywordIndex.js +289 -289
  8. package/src/catalogue/listQuery.js +100 -100
  9. package/src/catalogue/pagination.js +57 -57
  10. package/src/catalogue/searchLanguage.js +32 -32
  11. package/src/catalogue/timeline.js +220 -220
  12. package/src/composables/useCatalogueData.js +141 -141
  13. package/src/composables/useDataPackage.js +98 -98
  14. package/src/composables/useEntities.js +100 -100
  15. package/src/composables/useRecordLanguage.js +71 -71
  16. package/src/conventions.js +252 -148
  17. package/src/createViewer.js +29 -29
  18. package/src/i18n/I18nText.vue +17 -17
  19. package/src/i18n/I18nTextInline.vue +17 -17
  20. package/src/i18n/index.js +88 -88
  21. package/src/i18n/language.js +127 -127
  22. package/src/i18n/markdown.js +265 -265
  23. package/src/index.js +51 -50
  24. package/src/injectionKeys.js +1 -1
  25. package/src/languages.js +49 -49
  26. package/src/legacy/index.js +58 -58
  27. package/src/record/collectionTree.js +307 -307
  28. package/src/record/derivations.js +154 -154
  29. package/src/record/partners.js +87 -87
  30. package/src/record/recordSheet.js +231 -231
  31. package/src/rights.js +53 -53
  32. package/src/router/index.js +138 -138
  33. package/src/siteConfig.js +37 -37
  34. package/src/styles/base.css +49 -49
  35. package/src/testing/checkOfferedLanguages.js +67 -67
  36. package/src/testing/index.js +3 -3
  37. package/src/testing/smoke.js +103 -103
  38. package/src/testing/viteConfig.js +72 -90
  39. package/src/views/DetailView.vue +53 -53
  40. package/src/views/HomeView.vue +20 -20
  41. package/src/views/ListView.vue +61 -61
  42. package/src/views/NotFoundView.vue +8 -8
  43. package/src/vite.js +8 -8
@@ -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
- // Three small conventions each website had written for itself.
7
-
8
- // ── Project names ──────────────────────────────────────────────────────────
9
- //
10
- // `mwnf3.projectnames`, the whole table rather than the projects a website
11
- // happens to borrow from, since that set moves with every reimport. The key
12
- // on the left is data; the entry on the right is a shared text
13
- // (`core.project.*` in viewer-i18n), written out so a reader can see the
14
- // eight this asks for. A key that is not here reads as itself.
15
- export const PROJECT_ENTRIES = Object.freeze({
16
- ISL: 'core.project.islamicArt',
17
- EPM: 'core.project.explorePartners',
18
- DBA: 'core.project.baroqueArt',
19
- BAR: 'core.project.baroqueArt',
20
- AWE: 'core.project.sharingHistory',
21
- awe: 'core.project.sharingHistory',
22
- DCA: 'core.project.carpetArt',
23
- DGA: 'core.project.glassArt',
24
- EXTHE: 'core.project.tableIsSet',
25
- GALLERIES: 'core.project.galleries',
26
- })
27
-
28
- /** The name of a project by its legacy key, through `t`; the key itself when unknown. */
29
- export function projectName(key, t) {
30
- const entry = PROJECT_ENTRIES[key]
31
- return entry ? t(entry) : (key ?? '')
32
- }
33
-
34
- /** `projectName` bound to the installed texts, for a component. */
35
- export function useProjectName() {
36
- const { t } = useI18n()
37
- return (key) => projectName(key, t)
38
- }
39
-
40
- // A member is borrowed from the MWNF project that originally published it,
41
- // and legacy names and colours that project on the item sheet and in the
42
- // results grid — two separate mappings, because legacy keeps them separate:
43
- // the name is per project key (`PROJECT_ENTRIES` above), the colour is per
44
- // project *family*, so ISL and EPM share a swatch and every exhibition
45
- // shares another. The two exhibition sites re-hard-coded this table as
46
- // English literals next to their own copy of the name table, which is why a
47
- // project name translates on the galleries and not on the exhibitions.
48
- export const PROJECT_FAMILIES = Object.freeze({
49
- ISL: 'ISLandEPM',
50
- EPM: 'ISLandEPM',
51
- DBA: 'DBA',
52
- BAR: 'DBA',
53
- AWE: 'AWE',
54
- awe: 'AWE',
55
- DCA: 'DCA',
56
- DGA: 'DGA',
57
- EXTHE: 'EXH',
58
- GALLERIES: 'Galleries',
59
- })
60
-
61
- /** A project's family by its legacy key — legacy's own class names, so a
62
- * site's CSS reads as the stylesheet it was copied from. A key with no
63
- * entry falls back to itself, the same rule `projectName` applies. */
64
- export function projectFamily(key) {
65
- return PROJECT_FAMILIES[key] ?? (key ?? '')
66
- }
67
-
68
- // ── The section a route belongs to ─────────────────────────────────────────
69
- //
70
- // A route says what section it is (`meta: { section: 'collection' }`), and
71
- // the shell reads it here for the banner title or the active menu entry.
72
- // Four shells derived the same thing from `route.path.startsWith(…)` chains
73
- // of nine branches each; a route already knows.
74
- export function useSection() {
75
- const route = useRoute()
76
- return computed(() => String(route.meta?.section ?? ''))
77
- }
78
-
79
- // ── A featured record ──────────────────────────────────────────────────────
80
-
81
- // mulberry32 — a small seedable generator, so a test can pin the pick.
82
- function seeded(seed) {
83
- let a = seed >>> 0
84
- return () => {
85
- a = (a + 0x6d2b79f5) >>> 0
86
- let t = a
87
- t = Math.imul(t ^ (t >>> 15), t | 1)
88
- t ^= t + Math.imul(t ^ (t >>> 7), t | 61)
89
- return ((t ^ (t >>> 14)) >>> 0) / 4294967296
90
- }
91
- }
92
-
93
- /**
94
- * One record of `entity` chosen at random for a landing page's spotlight —
95
- * among those with an image when `withImage` is set (the default), among
96
- * all when none has one. Null until the entity is loaded. The pick is made
97
- * once per visit and again only when the records change; `seed` makes it
98
- * deterministic.
99
- */
100
- export function useFeaturedRecord(entity, { withImage = true, seed, images = 'images' } = {}) {
101
- const records = entityRef(entity)
102
- const random = seed == null ? Math.random : seeded(seed)
103
- return computed(() => {
104
- const all = toValue(records) ?? []
105
- if (all.length === 0) return null
106
- const pool = withImage ? all.filter((record) => (record?.[images]?.length ?? 0) > 0) : all
107
- const candidates = pool.length > 0 ? pool : all
108
- return candidates[Math.floor(random() * candidates.length)] ?? null
109
- })
110
- }
111
-
112
- // ── A route's `meta.entities` ────────────────────────────────────────────
113
-
114
- /**
115
- * `sectionMeta(chrome)` returns a `meta(section, ...entities)` helper: a
116
- * route's `meta: { section, entities: [...chrome, ...entities] }` in one
117
- * call. `chrome` is the entities every page of the site loads regardless of
118
- * what it is (the DXA sites' `['gallery', 'items', 'partners', 'countries']`
119
- * — the shell reads them on every route); a route names on top of that only
120
- * the entities its own page adds. Four DXA configs each wrote this same
121
- * four-line closure next to their own `chrome` list.
122
- */
123
- export function sectionMeta(chrome = []) {
124
- return (section, ...entities) => ({ section, entities: [...chrome, ...entities] })
125
- }
126
-
127
- // ── The MWNF portal ─────────────────────────────────────────────────────
128
-
129
- /**
130
- * The addresses every DXA `dataset.config.js` repeats under `links` —
131
- * the portal and the sibling standalone sites a gallery or an exhibition
132
- * links out to. Frozen: a site's own `links` may add to this object but
133
- * copying it is what this replaces.
134
- */
135
- export const mwnfLinks = Object.freeze({
136
- portal: 'https://www.museumwnf.org',
137
- galleries: 'https://galleries.museumwnf.org',
138
- myCollection: 'https://www.museumwnf.org/mycollection/index.php',
139
- about: 'https://www.museumwnf.org/about',
140
- contact: 'https://www.museumwnf.org/about/contact',
141
- legalNotice: 'https://www.museumwnf.org/about/legal-notice',
142
- credits: 'https://www.museumwnf.org/about/credits',
143
- cookies: 'https://www.museumwnf.org/about/cookies',
144
- overallDatabase: 'https://www.museumwnf.org/database_searchform.php',
145
- islamicArt: 'https://islamicart.museumwnf.org',
146
- baroqueArt: 'https://baroqueart.museumwnf.org',
147
- sharingHistory: 'https://sharinghistory.museumwnf.org',
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
+ })
@@ -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
+ }
@@ -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>