@opensalt/ob3-definer 1.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.
Files changed (45) hide show
  1. package/.vscode/extensions.json +3 -0
  2. package/LICENSE +21 -0
  3. package/README.md +86 -0
  4. package/dist/favicon.svg +7 -0
  5. package/dist/index.html +20 -0
  6. package/dist/ob3-definer.css +1 -0
  7. package/dist/ob3-definer.js +32 -0
  8. package/formkit.config.js +27 -0
  9. package/index.html +19 -0
  10. package/jsconfig.json +8 -0
  11. package/package.json +35 -0
  12. package/public/favicon.svg +7 -0
  13. package/src/App.vue +19 -0
  14. package/src/assets/base.css +86 -0
  15. package/src/assets/main.css +24 -0
  16. package/src/assets/style.scss +33 -0
  17. package/src/components/AchievementCriteria.vue +78 -0
  18. package/src/components/AchievementDefiner.vue +212 -0
  19. package/src/components/AchievementImage.vue +116 -0
  20. package/src/components/AchievementType.vue +111 -0
  21. package/src/components/AdditionalTab.vue +32 -0
  22. package/src/components/AddressComponent.vue +118 -0
  23. package/src/components/AlignmentComponent.vue +141 -0
  24. package/src/components/AlignmentsComponent.vue +13 -0
  25. package/src/components/AlignmentsTab.vue +18 -0
  26. package/src/components/BasicTab.vue +55 -0
  27. package/src/components/CreatorProfile.vue +166 -0
  28. package/src/components/CriterionLevels.vue +97 -0
  29. package/src/components/DetailTab.vue +72 -0
  30. package/src/components/IndividualName.vue +63 -0
  31. package/src/components/MarkdownRenderer.vue +20 -0
  32. package/src/components/OtherIdentifiers.vue +116 -0
  33. package/src/components/RelatedList.vue +89 -0
  34. package/src/components/ResultDescription.vue +120 -0
  35. package/src/components/ResultType.vue +94 -0
  36. package/src/components/TagList.vue +121 -0
  37. package/src/components/ValueList.vue +144 -0
  38. package/src/inputs/innerLabelTextInput.js +62 -0
  39. package/src/inputs/innerLabelTextareaInput.js +57 -0
  40. package/src/inputs/selectInputGroup.js +76 -0
  41. package/src/main.js +50 -0
  42. package/src/stores/credential.js +292 -0
  43. package/test-index.html +17 -0
  44. package/trial-key +3 -0
  45. package/vite.config.js +39 -0
@@ -0,0 +1,97 @@
1
+ <script setup>
2
+ import AlignmentComponent from "@/components/AlignmentComponent.vue";
3
+
4
+ const levels = defineModel({ default: {} });
5
+ </script>
6
+
7
+ <template>
8
+ <FormKit
9
+ #default="{ items, node, value }"
10
+ type="list"
11
+ v-model="levels"
12
+ name="rubricCriterionLevel"
13
+ dynamic
14
+ >
15
+ <h5>Rubric Criterion Levels
16
+ <button type="button" @click="() => node.input(value.concat({}))" class="btn btn-sm btn-primary ms-3">
17
+ Add Level
18
+ </button>
19
+ </h5>
20
+
21
+ <FormKit
22
+ type="group"
23
+ wrapper-class="card"
24
+ v-for="(item, index) in items"
25
+ :key="item"
26
+ :index="index"
27
+ >
28
+ <div class="card mb-3">
29
+ <h5 class="card-header">Level {{index+1}}
30
+ <button type="button" @click="() => node.input(value.filter((_, i) => i !== index))" class="btn btn-secondary btn-sm float-end">
31
+ Remove
32
+ </button>
33
+ </h5>
34
+
35
+ <div class="card-body">
36
+ <FormKit
37
+ type="hidden"
38
+ name="type"
39
+ :value="[ 'RubricCriterionLevel' ]"
40
+ />
41
+
42
+ <FormKit
43
+ type="innerLabelTextInput"
44
+ label="ID"
45
+ name="id"
46
+ inner-class="input-group"
47
+ label-class="input-group-text"
48
+ validation="required:trim"
49
+ wrapper-class="required"
50
+ />
51
+
52
+ <FormKit
53
+ type="innerLabelTextInput"
54
+ label="Name"
55
+ name="name"
56
+ inner-class="input-group"
57
+ label-class="input-group-text"
58
+ validation="required:trim"
59
+ wrapper-class="required"
60
+ />
61
+
62
+ <FormKit
63
+ type="innerLabelTextareaInput"
64
+ label="Description"
65
+ name="description"
66
+ inner-class="input-group"
67
+ label-class="input-group-text"
68
+ />
69
+
70
+ <FormKit
71
+ type="innerLabelTextInput"
72
+ label="Level"
73
+ name="level"
74
+ inner-class="input-group"
75
+ label-class="input-group-text"
76
+ />
77
+
78
+ <FormKit
79
+ type="innerLabelTextInput"
80
+ label="Points"
81
+ name="points"
82
+ inner-class="input-group"
83
+ label-class="input-group-text"
84
+ />
85
+
86
+ <AlignmentComponent v-model="levels[index].alignment"/>
87
+ </div>
88
+ </div>
89
+ </FormKit>
90
+ </FormKit>
91
+ </template>
92
+
93
+ <style>
94
+ .input-group>.form-label {
95
+ margin-bottom: 0;
96
+ }
97
+ </style>
@@ -0,0 +1,72 @@
1
+ <script setup>
2
+ const credential = defineModel({ default: {
3
+ humanCode: null,
4
+ inLanguage: null,
5
+ version: null,
6
+ creditsAvailable: null,
7
+ additionalType: null,
8
+ } })
9
+ </script>
10
+
11
+ <template>
12
+ <div class="tab-pane fade show active" id="tab-detail" role="tabpanel" aria-labelledby="detail-tab">
13
+ <FormKit
14
+ type="text"
15
+ label="Human Code"
16
+ v-model="credential.humanCode"
17
+ name="humanCode"
18
+ help="The code, generally human readable, associated with an achievement."
19
+ />
20
+
21
+ <FormKit
22
+ type="text"
23
+ label="In Language"
24
+ v-model="credential.inLanguage"
25
+ name="inLanguage"
26
+ help="The language this credential is in as a BCP47 language code, such as 'en', 'en-US', or 'es-MX'"
27
+ :validation="[['matches', '/^[a-z]{2,4}(-[A-Z][a-z]{3})?(-([A-Z]{2}|[0-9]{3}))?$/']]"
28
+ :validation-messages="{
29
+ 'matches': 'The language must formatted as a BCP47 language code, such as \'en\', \'en-US\', or \'es-MX\'.'
30
+ }"
31
+ />
32
+
33
+ <FormKit
34
+ type="text"
35
+ label="Version"
36
+ v-model="credential.version"
37
+ name="version"
38
+ help="The version property allows issuers to set a version string for an Achievement. This is particularly useful when replacing a previous version with an update."
39
+ />
40
+
41
+ <FormKit
42
+ type="number"
43
+ label="Credits Available"
44
+ number
45
+ name="creditsAvailable"
46
+ v-model="credential.creditsAvailable"
47
+ help="Credit hours associated with this entity, or credit hours possible. For example 3.0."
48
+ />
49
+
50
+ <FormKit
51
+ type="text"
52
+ label="Specialization"
53
+ v-model="credential.specialization"
54
+ name="specialization"
55
+ help="Name given to the focus, concentration, or specific area of study defined in the achievement. Examples include 'Entrepreneurship', 'Technical Communication', and 'Finance'."
56
+ />
57
+
58
+ <FormKit
59
+ type="text"
60
+ label="Field of Study"
61
+ v-model="credential.fieldOfStudy"
62
+ name="fieldOfStudy"
63
+ help="Category, subject, area of study, discipline, or general branch of knowledge. Examples include Business, Education, Psychology, and Technology."
64
+ />
65
+ </div>
66
+ </template>
67
+
68
+ <style scoped>
69
+ #criteria-preview {
70
+ min-height: 250px;
71
+ }
72
+ </style>
@@ -0,0 +1,63 @@
1
+ <script setup>
2
+
3
+ </script>
4
+
5
+ <template>
6
+ <FormKit
7
+ type="group"
8
+ >
9
+ <FormKit
10
+ type="text"
11
+ label="Given Name"
12
+ name="givenName"
13
+ help="Given name. In the western world, often referred to as the 'first name' of a person."
14
+ />
15
+
16
+ <FormKit
17
+ type="text"
18
+ label="Additional Name"
19
+ name="additionalName"
20
+ help="Additional name. Includes what is often referred to as 'middle name' in the western world."
21
+ />
22
+
23
+ <FormKit
24
+ type="text"
25
+ label="Family Name Prefix"
26
+ name="familyNamePrefix"
27
+ help="Family name prefix. As used in some locales, this is the leading part of a family name (e.g. 'de' in the name 'de Boer')."
28
+ />
29
+
30
+ <FormKit
31
+ type="text"
32
+ label="Family Name"
33
+ name="familyName"
34
+ help="Family name. In the western world, often referred to as the 'last name' of a person."
35
+ />
36
+
37
+ <FormKit
38
+ type="text"
39
+ label="Patronymic Name"
40
+ name="patronymicName"
41
+ help="Patronynmic name."
42
+ v-if="false"
43
+ />
44
+
45
+ <FormKit
46
+ type="text"
47
+ label="Honorific Prefix"
48
+ name="honorificPrefix"
49
+ help="Honorific prefix(es) preceding a person's name (e.g. 'Dr', 'Mrs' or 'Mr')."
50
+ />
51
+
52
+ <FormKit
53
+ type="text"
54
+ label="Honorific Suffix"
55
+ name="honorificSuffix"
56
+ help="Honorific suffix(es) following a person's name (e.g. 'M.D, PhD')."
57
+ />
58
+ </FormKit>
59
+ </template>
60
+
61
+ <style scoped>
62
+
63
+ </style>
@@ -0,0 +1,20 @@
1
+ <script setup>
2
+ import MarkdownIt from "markdown-it";
3
+
4
+ const markdown = MarkdownIt();
5
+
6
+ const props = defineProps({
7
+ source: {
8
+ type: String,
9
+ default: ""
10
+ }
11
+ });
12
+ </script>
13
+
14
+ <template>
15
+ <div class="mb-3" v-html="markdown.render(source)" />
16
+ </template>
17
+
18
+ <style scoped>
19
+
20
+ </style>
@@ -0,0 +1,116 @@
1
+ <script setup>
2
+ const props = defineProps({
3
+ help: {
4
+ type: String,
5
+ default: 'Other identifiers for this credential.'
6
+ }
7
+ });
8
+
9
+ const identifiers = defineModel({ default: [] });
10
+ </script>
11
+
12
+ <template>
13
+ <FormKit
14
+ #default="{ items, node, value }"
15
+ type="list"
16
+ v-model="identifiers"
17
+ name="otherIdentifier"
18
+ dynamic
19
+ >
20
+ <h5>Other Identifiers
21
+ <button type="button" @click="() => node.input(value.concat({}))" class="btn btn-sm btn-primary ms-3">
22
+ Add Identifier
23
+ </button>
24
+ </h5>
25
+ <p class="form-text">{{ props.help }}</p>
26
+
27
+ <FormKit
28
+ type="group"
29
+ v-for="(item, index) in items"
30
+ :key="item"
31
+ :index="index"
32
+ >
33
+ <div class="card mb-3">
34
+ <h5 class="card-header">Other Identifier {{index+1}}
35
+ <button type="button" @click="() => node.input(value.filter((_, i) => i !== index))" class="btn btn-secondary btn-sm float-end">
36
+ Remove
37
+ </button>
38
+ </h5>
39
+
40
+ <div class="card-body pb-0">
41
+ <FormKit
42
+ type="hidden"
43
+ name="type"
44
+ :value="'IdentifierEntry'"
45
+ />
46
+
47
+ <FormKit
48
+ type="innerLabelTextInput"
49
+ label="Identifier"
50
+ name="identifier"
51
+ aria-label="Identifier"
52
+ inner-class="input-group"
53
+ label-class="input-group-text"
54
+ wrapper-class="required"
55
+ validation="required:trim"
56
+ />
57
+ <FormKit
58
+ type="selectInputGroup"
59
+ label="Identifier Type"
60
+ name="identifierType"
61
+ inner-class="input-group"
62
+ label-class="input-group-text"
63
+ input-class="$reset formkit-input form-select"
64
+ wrapper-class="required"
65
+ placeholder="Select the Identifier Type"
66
+ validation="required"
67
+ :options="{
68
+ 'name': 'name',
69
+ 'sourcedId': 'sourcedId',
70
+ 'systemId': 'systemId',
71
+ 'productId': 'productId',
72
+ 'userName': 'userName',
73
+ 'accountId': 'accountId',
74
+ 'emailAddress': 'emailAddress',
75
+ 'nationalIdentityNumber': 'nationalIdentityNumber',
76
+ 'isbn': 'isbn',
77
+ 'issn': 'issn',
78
+ 'lisSourcedId': 'lisSourcedId',
79
+ 'oneRosterSourcedId': 'oneRosterSourcedId',
80
+ 'sisSourcedId': 'sisSourcedId',
81
+ 'ltiContextId': 'ltiContextId',
82
+ 'ltiDeploymentId': 'ltiDeploymentId',
83
+ 'ltiToolId': 'ltiToolId',
84
+ 'ltiPlatformId': 'ltiPlatformId',
85
+ 'ltiUserId': 'ltiUserId',
86
+ 'identifier': 'identifier',
87
+ 'other': 'Other (ext:)',
88
+ }"
89
+ />
90
+ <FormKit
91
+ v-if="value[index].identifierType === 'other'"
92
+ type="innerLabelTextInput"
93
+ label="Extended Type"
94
+ name="typeExt"
95
+ aria-label="Extended Identifier Type"
96
+ inner-class="input-group ms-3 me-5 pe-3"
97
+ outer-class="$reset"
98
+ label-class="visually-hidden"
99
+ before="ext:"
100
+ :validation="[['matches', '/^[a-z|A-Z|0-9|.|-|_]+$/']]"
101
+ >
102
+ <template #prefix>
103
+ <span class="input-group-text" aria-label="ext:">ext:</span>
104
+ </template>
105
+ </FormKit>
106
+ </div>
107
+ </div>
108
+ </FormKit>
109
+ </FormKit>
110
+ </template>
111
+
112
+ <style>
113
+ .input-group>.form-label {
114
+ margin-bottom: 0;
115
+ }
116
+ </style>
@@ -0,0 +1,89 @@
1
+ <script setup>
2
+ import {ref, watch} from "vue";
3
+
4
+ const emit = defineEmits(['change']);
5
+
6
+ const related = ref([]);
7
+
8
+ watch(related, (newRelated) => {
9
+ //credential.alignment = newAlignment;
10
+ emit('change', newRelated);
11
+ });
12
+ </script>
13
+
14
+ <template>
15
+ <FormKit
16
+ #default="{ items, node, value }"
17
+ type="list"
18
+ v-model="related"
19
+ name="alignment"
20
+ dynamic
21
+ >
22
+ <h5>Related Credentials
23
+ <button type="button" @click="() => node.input(value.concat({}))" class="btn btn-sm btn-primary ms-3">
24
+ Add Related Credential
25
+ </button>
26
+ </h5>
27
+ <p class="form-text">A list of credentials related to this one.</p>
28
+
29
+ <FormKit
30
+ type="group"
31
+ wrapper-class="card"
32
+ v-for="(item, index) in items"
33
+ :key="item"
34
+ :index="index"
35
+ >
36
+ <div class="card mb-3">
37
+ <h5 class="card-header">Credential {{index+1}}
38
+ <button type="button" @click="() => node.input(value.filter((_, i) => i !== index))" class="btn btn-secondary btn-sm float-end">
39
+ Remove
40
+ </button>
41
+ </h5>
42
+
43
+ <div class="card-body pb-0">
44
+ <FormKit
45
+ type="hidden"
46
+ name="type"
47
+ :value="[ 'Related' ]"
48
+ />
49
+
50
+ <FormKit
51
+ type="innerLabelTextInput"
52
+ label="ID"
53
+ name="id"
54
+ inner-class="input-group"
55
+ label-class="input-group-text"
56
+ wrapper-class="required"
57
+ validation="required:trim"
58
+ help="The URI of the related achievement."
59
+ />
60
+
61
+ <FormKit
62
+ type="innerLabelTextInput"
63
+ label="Language"
64
+ name="inLanguage"
65
+ inner-class="input-group"
66
+ label-class="input-group-text"
67
+ :validation="[['matches', '/^[a-z]{2,4}(-[A-Z][a-z]{3})?(-([A-Z]{2}|[0-9]{3}))?$/']]"
68
+ help="The language of the related achievement as a BCP47 language code."
69
+ />
70
+
71
+ <FormKit
72
+ type="innerLabelTextInput"
73
+ label="Version"
74
+ name="version"
75
+ inner-class="input-group"
76
+ label-class="input-group-text"
77
+ help="The version of the related achievement."
78
+ />
79
+ </div>
80
+ </div>
81
+ </FormKit>
82
+ </FormKit>
83
+ </template>
84
+
85
+ <style>
86
+ .input-group>.form-label {
87
+ margin-bottom: 0;
88
+ }
89
+ </style>
@@ -0,0 +1,120 @@
1
+ <script setup>
2
+ import AlignmentComponent from "@/components/AlignmentComponent.vue";
3
+ import ValueList from "@/components/ValueList.vue";
4
+ import CriterionLevels from "@/components/CriterionLevels.vue";
5
+ import ResultType from "@/components/ResultType.vue";
6
+
7
+ const results = defineModel({ default: [] });
8
+ </script>
9
+
10
+ <template>
11
+ <FormKit
12
+ #default="{ items, node, value }"
13
+ type="list"
14
+ name="resultDescription"
15
+ v-model="results"
16
+ dynamic
17
+ >
18
+ <h5 class="mb-4">Result Descriptions
19
+ <button type="button" @click="() => node.input(value.concat({}))" class="btn btn-sm btn-primary ms-3">
20
+ Add Result Description
21
+ </button>
22
+ </h5>
23
+
24
+ <FormKit
25
+ type="group"
26
+ wrapper-class="card"
27
+ v-for="(item, index) in items"
28
+ :key="item"
29
+ :index="index"
30
+ >
31
+ <div class="card mb-3">
32
+ <h5 class="card-header">Result Description {{index+1}}
33
+ <button type="button" @click="() => node.input(value.filter((_, i) => i !== index))" class="btn btn-secondary btn-sm float-end">
34
+ Remove
35
+ </button>
36
+ </h5>
37
+
38
+ <div class="card-body">
39
+ <FormKit
40
+ type="innerLabelTextInput"
41
+ label="ID"
42
+ name="id"
43
+ inner-class="input-group"
44
+ label-class="input-group-text"
45
+ wrapper-class="required"
46
+ validation="required:trim"
47
+ help="The unique URI for this result description. Required so a result can link to this result description."
48
+ />
49
+
50
+ <FormKit
51
+ type="hidden"
52
+ name="type"
53
+ :value="[ 'ResultDescription' ]"
54
+ />
55
+
56
+ <FormKit
57
+ type="innerLabelTextInput"
58
+ label="Name"
59
+ name="name"
60
+ inner-class="input-group"
61
+ label-class="input-group-text"
62
+ wrapper-class="required"
63
+ validation="required:trim"
64
+ help="The name of the result."
65
+ />
66
+
67
+ <ResultType v-model="results[index].resultType" />
68
+
69
+ <ValueList v-model="results[index].allowedValue"/>
70
+
71
+ <FormKit
72
+ type="innerLabelTextInput"
73
+ label="Minimum Value"
74
+ name="valueMin"
75
+ inner-class="input-group"
76
+ label-class="input-group-text"
77
+ help="The minimum possible `value` that may be asserted in a linked result."
78
+ />
79
+
80
+ <FormKit
81
+ type="innerLabelTextInput"
82
+ label="Maximum Value"
83
+ name="valueMax"
84
+ inner-class="input-group"
85
+ label-class="input-group-text"
86
+ help="The maximum possible `value` that may be asserted in a linked result."
87
+ />
88
+
89
+ <CriterionLevels v-model="results[index].rubricCriterionLevel"/>
90
+
91
+ <FormKit
92
+ type="innerLabelTextInput"
93
+ label="Required Level"
94
+ name="requiredLevel"
95
+ inner-class="input-group"
96
+ label-class="input-group-text"
97
+ help="The `id` of the rubric criterion level required to pass as determined by the achievement creator."
98
+ />
99
+
100
+ <FormKit
101
+ type="innerLabelTextInput"
102
+ label="Required Value"
103
+ name="requiredValue"
104
+ inner-class="input-group"
105
+ label-class="input-group-text"
106
+ help="A value from `allowedValue` or within the range of `valueMin` to `valueMax` required to pass as determined by the achievement creator."
107
+ />
108
+
109
+ <AlignmentComponent v-model="results[index].alignment"/>
110
+ </div>
111
+ </div>
112
+ </FormKit>
113
+ </FormKit>
114
+ </template>
115
+
116
+ <style>
117
+ .input-group>.form-label {
118
+ margin-bottom: 0;
119
+ }
120
+ </style>
@@ -0,0 +1,94 @@
1
+ <script setup>
2
+ import {onMounted, ref, watch} from "vue";
3
+
4
+ const model = defineModel({ default: ''});
5
+ const selectedType = ref('');
6
+ const typeExt = ref('');
7
+
8
+ onMounted(() => {
9
+ if (model.value?.substring(0, 4) === 'ext:') {
10
+ selectedType.value = 'other';
11
+ typeExt.value = model.value.substring(4);
12
+
13
+ return;
14
+ }
15
+
16
+ selectedType.value = model.value ? model.value : '';
17
+ typeExt.value = '';
18
+ });
19
+
20
+ watch(selectedType, (value) => {
21
+ if (value === 'other') {
22
+ model.value = 'ext:'+typeExt.value;
23
+
24
+ return;
25
+ }
26
+
27
+ model.value = value;
28
+ });
29
+
30
+ watch(typeExt, (value) => {
31
+ if (selectedType.value === 'other') {
32
+ model.value = 'ext:'+value;
33
+
34
+ return;
35
+ }
36
+
37
+ model.value = selectedType.value;
38
+ });
39
+ </script>
40
+
41
+ <template>
42
+ <FormKit
43
+ type="selectInputGroup"
44
+ label="Result Type"
45
+ v-model="selectedType"
46
+ name="selectedType"
47
+ inner-class="input-group"
48
+ label-class="input-group-text"
49
+ input-class="$reset formkit-input form-select"
50
+ wrapper-class="required"
51
+ placeholder="Select Result Type"
52
+ validation="required"
53
+ :options="{
54
+ 'GradePointAverage': 'Grade Point Average',
55
+ 'LetterGrade': 'Letter Grade',
56
+ 'Percent': 'Percent',
57
+ 'PerformanceLevel': 'Performance Level',
58
+ 'PredictedScore': 'Predicted Score',
59
+ 'RawScore': 'Raw Score',
60
+ 'Result': 'Result',
61
+ 'RubricCriterion': 'Rubric Criterion',
62
+ 'RubricCriterionLevel': 'Rubric Criterion Level',
63
+ 'RubricScore': 'Rubric Score',
64
+ 'ScaledScore': 'Scaled Score',
65
+ 'Status': 'Status',
66
+ 'other': 'Other (ext:)'
67
+ }"
68
+ help="The type of result this description represents. This is an extensible enumerated vocabulary."
69
+ >
70
+ <template #suffix>
71
+ <FormKit
72
+ v-if="selectedType === 'other'"
73
+ type="innerLabelTextInput"
74
+ label="Extended Type"
75
+ v-model="typeExt"
76
+ name="typeExt"
77
+ aria-label="Extended Result Type"
78
+ inner-class="input-group ms-3 me-5 pe-3"
79
+ outer-class="$reset"
80
+ label-class="visually-hidden"
81
+ before="ext:"
82
+ :validation="[['matches', '/^[a-z|A-Z|0-9|.|-|_]+$/'], ['required']]"
83
+ >
84
+ <template #prefix>
85
+ <span class="input-group-text" aria-label="ext:">ext:</span>
86
+ </template>
87
+ </FormKit>
88
+ </template>
89
+ </FormKit>
90
+ </template>
91
+
92
+ <style scoped>
93
+
94
+ </style>