@bedrock/vue-vc 5.0.0 → 5.1.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 +1 -2
- package/components/CredentialBase.vue +8 -2
- package/components/CredentialDetail.vue +1 -1
- package/components/CredentialField.vue +7 -3
- package/components/CredentialSwitch.vue +1 -1
- package/components/DynamicImage.vue +3 -6
- package/components/credentialCommon.js +21 -5
- package/components/credentialDefinitions.js +72 -0
- package/lib/index.js +1 -1
- package/package.json +6 -10
package/LICENSE.md
CHANGED
|
@@ -20,7 +20,9 @@
|
|
|
20
20
|
descriptionOverride : description"
|
|
21
21
|
:value-class="`text-right
|
|
22
22
|
${textColor ? '':' text-grey-7'}
|
|
23
|
-
${dense ? ' text-caption':' text-caption'}`"
|
|
23
|
+
${dense ? ' text-caption':' text-caption'}`"
|
|
24
|
+
:max-width="maxWidth"
|
|
25
|
+
lines="2" />
|
|
24
26
|
</slot>
|
|
25
27
|
</div>
|
|
26
28
|
<q-dialog v-model="state.details">
|
|
@@ -42,7 +44,7 @@
|
|
|
42
44
|
|
|
43
45
|
<script setup>
|
|
44
46
|
/*!
|
|
45
|
-
* Copyright (c) 2018-
|
|
47
|
+
* Copyright (c) 2018-2026 Digital Bazaar, Inc.
|
|
46
48
|
*/
|
|
47
49
|
import {computed, defineProps, reactive, toRef, unref} from 'vue';
|
|
48
50
|
import CredentialField from './CredentialField.vue';
|
|
@@ -94,6 +96,10 @@ const props = defineProps({
|
|
|
94
96
|
descriptionOverride: {
|
|
95
97
|
type: String,
|
|
96
98
|
default: ''
|
|
99
|
+
},
|
|
100
|
+
maxWidth: {
|
|
101
|
+
type: String,
|
|
102
|
+
default: '225px'
|
|
97
103
|
}
|
|
98
104
|
});
|
|
99
105
|
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
|
|
22
22
|
<script setup>
|
|
23
23
|
/*!
|
|
24
|
-
* Copyright (c) 2018-
|
|
24
|
+
* Copyright (c) 2018-2026 Digital Bazaar, Inc.
|
|
25
25
|
*/
|
|
26
26
|
import {computed, defineProps, toRef, unref} from 'vue';
|
|
27
27
|
import CredentialField from './CredentialField.vue';
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<q-item-section>
|
|
5
5
|
<q-item-label
|
|
6
6
|
class="cf-value"
|
|
7
|
-
style="
|
|
7
|
+
:style="{maxWidth: maxWidth}"
|
|
8
8
|
:class="valueClass"
|
|
9
9
|
:lines="lines">
|
|
10
10
|
{{value}}
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
<q-item-label
|
|
13
13
|
v-if="title"
|
|
14
14
|
class="cf-title"
|
|
15
|
-
style="
|
|
15
|
+
:style="{maxWidth: maxWidth}"
|
|
16
16
|
:class="titleClass"
|
|
17
17
|
:lines="lines">
|
|
18
18
|
{{title}}
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
|
|
25
25
|
<script setup>
|
|
26
26
|
/*!
|
|
27
|
-
* Copyright (c) 2018-
|
|
27
|
+
* Copyright (c) 2018-2026 Digital Bazaar, Inc.
|
|
28
28
|
*/
|
|
29
29
|
import {computed, defineProps} from 'vue';
|
|
30
30
|
|
|
@@ -48,6 +48,10 @@ const props = defineProps({
|
|
|
48
48
|
lines: {
|
|
49
49
|
type: String,
|
|
50
50
|
default: '1'
|
|
51
|
+
},
|
|
52
|
+
maxWidth: {
|
|
53
|
+
type: String,
|
|
54
|
+
default: '225px'
|
|
51
55
|
}
|
|
52
56
|
});
|
|
53
57
|
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
<script setup>
|
|
16
16
|
/*!
|
|
17
|
-
* Copyright (c) 2018-
|
|
17
|
+
* Copyright (c) 2018-2026 Digital Bazaar, Inc.
|
|
18
18
|
*/
|
|
19
19
|
import {computed, defineProps, ref} from 'vue';
|
|
20
20
|
import {useQuasar} from 'quasar';
|
|
@@ -47,13 +47,11 @@ const hasImageError = ref(false);
|
|
|
47
47
|
const props = defineProps({
|
|
48
48
|
src: {
|
|
49
49
|
type: String,
|
|
50
|
-
required: true
|
|
51
|
-
default: ''
|
|
50
|
+
required: true
|
|
52
51
|
},
|
|
53
52
|
size: {
|
|
54
53
|
type: String,
|
|
55
|
-
required: true
|
|
56
|
-
default: 'md'
|
|
54
|
+
required: true
|
|
57
55
|
}
|
|
58
56
|
});
|
|
59
57
|
|
|
@@ -157,6 +155,5 @@ img {
|
|
|
157
155
|
height: $xl;
|
|
158
156
|
border-radius: 4px;
|
|
159
157
|
}
|
|
160
|
-
|
|
161
158
|
}
|
|
162
159
|
</style>
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* Copyright (c) 2022 Digital Bazaar, Inc.
|
|
2
|
+
* Copyright (c) 2022-2026 Digital Bazaar, Inc.
|
|
3
3
|
*/
|
|
4
4
|
import {computed, unref} from 'vue';
|
|
5
|
+
import {resolveCredentialDefinitionField} from './credentialDefinitions.js';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Common utilities for credential components.
|
|
@@ -26,8 +27,16 @@ export function useCredentialCommon({credential}) {
|
|
|
26
27
|
});
|
|
27
28
|
|
|
28
29
|
const credentialImage = computed(() => {
|
|
29
|
-
const
|
|
30
|
-
|
|
30
|
+
const cred = unref(credential);
|
|
31
|
+
const {image = null, issuer} = cred;
|
|
32
|
+
// fall back to a vocabulary-specific image (e.g. OBv3's
|
|
33
|
+
// `credentialSubject.achievement.image`) when no top-level image is
|
|
34
|
+
// set; the resolved value may itself be a string URL or an image
|
|
35
|
+
// object (`.id` holds the URL), per the OBv3 vocabulary
|
|
36
|
+
const definitionImage = resolveCredentialDefinitionField(
|
|
37
|
+
{credential: cred, field: 'imagePointer'});
|
|
38
|
+
const definitionImageUrl = definitionImage?.id ?? definitionImage ?? null;
|
|
39
|
+
return image ?? definitionImageUrl ?? issuer?.image ?? issuer?.logo ?? '';
|
|
31
40
|
});
|
|
32
41
|
|
|
33
42
|
const issuerName = computed(() => {
|
|
@@ -36,8 +45,15 @@ export function useCredentialCommon({credential}) {
|
|
|
36
45
|
});
|
|
37
46
|
|
|
38
47
|
const credentialDescription = computed(() => {
|
|
39
|
-
const
|
|
40
|
-
|
|
48
|
+
const cred = unref(credential);
|
|
49
|
+
const {description = ''} = cred;
|
|
50
|
+
// fall back to a vocabulary-specific description (e.g. OBv3's
|
|
51
|
+
// `credentialSubject.achievement.description`) when no top-level
|
|
52
|
+
// description is set. Use `||`, not `??`, so an explicit empty string
|
|
53
|
+
// (not just a missing/undefined field) also triggers the fallback.
|
|
54
|
+
const definitionDescription = resolveCredentialDefinitionField(
|
|
55
|
+
{credential: cred, field: 'descriptionPointer'});
|
|
56
|
+
return description || definitionDescription || '';
|
|
41
57
|
});
|
|
42
58
|
|
|
43
59
|
return {
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2026 Digital Bazaar, Inc.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Generic, vocabulary-specific fallbacks for credential fields the core
|
|
6
|
+
* VCDM does not define a fixed location for (e.g. `image`, `description`).
|
|
7
|
+
* Each definition matches a credential via a map of JSON Pointer (RFC 6901)
|
|
8
|
+
* => expected value, and supplies JSON Pointers to resolve for fields it
|
|
9
|
+
* covers. A pointer whose target value is an array (e.g. `/type`) matches
|
|
10
|
+
* if the array includes the expected value.
|
|
11
|
+
*
|
|
12
|
+
* Matching on `/type` rather than `/@context` avoids breaking on
|
|
13
|
+
* vocabularies (like Open Badges) that mint a new context URL per minor
|
|
14
|
+
* version -- `type` values are stable across those.
|
|
15
|
+
*/
|
|
16
|
+
export const credentialDefinitions = [
|
|
17
|
+
{
|
|
18
|
+
// Open Badges v3 (OBv3) `OpenBadgeCredential`s.
|
|
19
|
+
matches: {
|
|
20
|
+
'/type': 'OpenBadgeCredential'
|
|
21
|
+
},
|
|
22
|
+
imagePointer: '/credentialSubject/achievement/image',
|
|
23
|
+
descriptionPointer: '/credentialSubject/achievement/description'
|
|
24
|
+
}
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Resolves a JSON Pointer (RFC 6901) against a value.
|
|
29
|
+
*
|
|
30
|
+
* @param {object} options - The options to use.
|
|
31
|
+
* @param {*} options.value - The value to resolve the pointer against.
|
|
32
|
+
* @param {string} [options.pointer] - The JSON Pointer to resolve.
|
|
33
|
+
*
|
|
34
|
+
* @returns {*} The resolved value, or `undefined` if the pointer is unset
|
|
35
|
+
* or any segment is missing.
|
|
36
|
+
*/
|
|
37
|
+
export function getPointerValue({value, pointer}) {
|
|
38
|
+
if(!pointer) {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
const segments = pointer.split('/').slice(1).map(
|
|
42
|
+
segment => segment.replace(/~1/g, '/').replace(/~0/g, '~'));
|
|
43
|
+
return segments.reduce((target, segment) => target?.[segment], value);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function _matchesDefinition({credential, matches}) {
|
|
47
|
+
return Object.entries(matches).every(([pointer, expected]) => {
|
|
48
|
+
const target = getPointerValue({value: credential, pointer});
|
|
49
|
+
return Array.isArray(target) ?
|
|
50
|
+
target.includes(expected) : target === expected;
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Finds the first credential definition matching `credential` and resolves
|
|
56
|
+
* the JSON Pointer named by `field` (e.g. `imagePointer`,
|
|
57
|
+
* `descriptionPointer`) against it.
|
|
58
|
+
*
|
|
59
|
+
* @param {object} options - The options to use.
|
|
60
|
+
* @param {object} options.credential - The credential to resolve a
|
|
61
|
+
* fallback value for.
|
|
62
|
+
* @param {string} options.field - The definition field naming the JSON
|
|
63
|
+
* Pointer to resolve.
|
|
64
|
+
*
|
|
65
|
+
* @returns {*} The resolved value, or `undefined` if no definition
|
|
66
|
+
* matches, or the matched definition does not set `field`.
|
|
67
|
+
*/
|
|
68
|
+
export function resolveCredentialDefinitionField({credential, field}) {
|
|
69
|
+
const definition = credentialDefinitions.find(
|
|
70
|
+
({matches}) => _matchesDefinition({credential, matches}));
|
|
71
|
+
return getPointerValue({value: credential, pointer: definition?.[field]});
|
|
72
|
+
}
|
package/lib/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bedrock/vue-vc",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Vue.js credential components for Bedrock Web Apps",
|
|
6
6
|
"exports": {
|
|
@@ -32,16 +32,12 @@
|
|
|
32
32
|
},
|
|
33
33
|
"homepage": "https://github.com/digitalbazaar/bedrock-vue-vc",
|
|
34
34
|
"peerDependencies": {
|
|
35
|
-
"@bedrock/quasar": "^10.0.
|
|
36
|
-
"@bedrock/web": "^3.
|
|
37
|
-
"vue": "^3.
|
|
35
|
+
"@bedrock/quasar": "^10.0.1",
|
|
36
|
+
"@bedrock/web": "^3.1.0",
|
|
37
|
+
"vue": "^3.5.42"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"eslint": "^
|
|
41
|
-
"eslint
|
|
42
|
-
"eslint-plugin-jsdoc": "^48.2.1",
|
|
43
|
-
"eslint-plugin-quasar": "^1.1.0",
|
|
44
|
-
"eslint-plugin-unicorn": "^51.0.1",
|
|
45
|
-
"eslint-plugin-vue": "^9.23.0"
|
|
40
|
+
"@digitalbazaar/eslint-config": "^9.0.0",
|
|
41
|
+
"eslint": "^10.9.1"
|
|
46
42
|
}
|
|
47
43
|
}
|