@bedrock/vue-vc 5.0.0 → 6.0.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 CHANGED
@@ -1,8 +1,7 @@
1
1
  Bedrock Non-Commercial License v1.0
2
2
  ===================================
3
3
 
4
- Copyright (c) 2011-2022 Digital Bazaar, Inc.
5
- All rights reserved.
4
+ Copyright (c) 2011-2026 Digital Bazaar, Inc.
6
5
 
7
6
  Summary
8
7
  =======
@@ -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-2022 Digital Bazaar, Inc. All rights reserved.
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-2023 Digital Bazaar, Inc. All rights reserved.
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="max-width: 225px;"
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="max-width: 225px;"
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-2022 Digital Bazaar, Inc. All rights reserved.
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-2022 Digital Bazaar, Inc. All rights reserved.
17
+ * Copyright (c) 2018-2026 Digital Bazaar, Inc.
18
18
  */
19
19
  import {computed, defineProps, unref} from 'vue';
20
20
  import {config, extend} from '@bedrock/web';
@@ -14,7 +14,7 @@
14
14
 
15
15
  <script setup>
16
16
  /*!
17
- * Copyright (c) 2018-2022 Digital Bazaar, Inc. All rights reserved.
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. All rights reserved.
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 {image = null, issuer} = unref(credential);
30
- return image ?? issuer?.image ?? issuer?.logo ?? '';
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 {description = ''} = unref(credential);
40
- return description;
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
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Copyright (c) 2018-2022 Digital Bazaar, Inc. All rights reserved.
2
+ * Copyright (c) 2018-2026 Digital Bazaar, Inc.
3
3
  */
4
4
  import './config.js';
5
5
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bedrock/vue-vc",
3
- "version": "5.0.0",
3
+ "version": "6.0.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.0",
36
- "@bedrock/web": "^3.0.0",
37
- "vue": "^3.4.21"
35
+ "@bedrock/quasar": "^11.0.0",
36
+ "@bedrock/web": "^3.1.0",
37
+ "vue": "^3.5.42"
38
38
  },
39
39
  "devDependencies": {
40
- "eslint": "^8.57.0",
41
- "eslint-config-digitalbazaar": "^5.0.1",
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
  }