@opensalt/ob3-definer 1.2.3 → 1.2.5

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/dist/index.html +3 -1
  2. package/dist/ob3-definer.js +11 -11
  3. package/package.json +4 -1
  4. package/.vscode/extensions.json +0 -3
  5. package/NOTES +0 -6
  6. package/Test Achievement-bug1.json +0 -42
  7. package/formkit.config.js +0 -27
  8. package/index-2.html +0 -22
  9. package/index.html +0 -19
  10. package/jsconfig.json +0 -8
  11. package/public/favicon.svg +0 -7
  12. package/src/App.vue +0 -23
  13. package/src/assets/base.css +0 -86
  14. package/src/assets/main.css +0 -24
  15. package/src/assets/style.scss +0 -33
  16. package/src/components/AchievementCriteria.vue +0 -78
  17. package/src/components/AchievementDefiner.vue +0 -223
  18. package/src/components/AchievementImage.vue +0 -167
  19. package/src/components/AchievementType.vue +0 -118
  20. package/src/components/AdditionalTab.vue +0 -33
  21. package/src/components/AddressComponent.vue +0 -118
  22. package/src/components/AlignmentComponent.vue +0 -103
  23. package/src/components/AlignmentType.vue +0 -98
  24. package/src/components/AlignmentsComponent.vue +0 -13
  25. package/src/components/AlignmentsTab.vue +0 -18
  26. package/src/components/BasicTab.vue +0 -55
  27. package/src/components/CreatorProfile.vue +0 -166
  28. package/src/components/CriterionLevels.vue +0 -97
  29. package/src/components/DetailTab.vue +0 -72
  30. package/src/components/IndividualName.vue +0 -63
  31. package/src/components/MarkdownRenderer.vue +0 -20
  32. package/src/components/OtherIdentifiers.vue +0 -116
  33. package/src/components/RelatedList.vue +0 -89
  34. package/src/components/ResultDescription.vue +0 -121
  35. package/src/components/ResultType.vue +0 -94
  36. package/src/components/TagList.vue +0 -121
  37. package/src/components/ValueList.vue +0 -144
  38. package/src/inputs/innerLabelTextInput.js +0 -62
  39. package/src/inputs/innerLabelTextareaInput.js +0 -57
  40. package/src/inputs/selectInputGroup.js +0 -76
  41. package/src/main.js +0 -57
  42. package/src/stores/credential.js +0 -292
  43. package/src/validation/uri.js +0 -13
  44. package/test-index.html +0 -17
  45. package/vite.config.js +0 -39
package/package.json CHANGED
@@ -1,11 +1,14 @@
1
1
  {
2
2
  "name": "@opensalt/ob3-definer",
3
- "version": "1.2.3",
3
+ "version": "1.2.5",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/opensalt/OB3DefinitionWidget.git"
8
8
  },
9
+ "files": [
10
+ "dist"
11
+ ],
9
12
  "type": "module",
10
13
  "description": "A widget to create an Open Badge achievement definition.",
11
14
  "main": "dist/ob3-definer.js",
@@ -1,3 +0,0 @@
1
- {
2
- "recommendations": ["Vue.volar"]
3
- }
package/NOTES DELETED
@@ -1,6 +0,0 @@
1
- # make updates
2
- # update package.json with updated version number
3
- # log into npmjs.com (roverwolf)
4
-
5
- yarn run build
6
- npm publish
@@ -1,42 +0,0 @@
1
- {
2
- "id": "https://bismarckstate.edu/academics/programs/computersupport/",
3
- "type": [
4
- "Achievement"
5
- ],
6
- "alignment": [],
7
- "achievementType": null,
8
- "creator": null,
9
- "creditsAvailable": null,
10
- "criteria": {
11
- "id": null,
12
- "narrative": "Test narrative"
13
- },
14
- "description": "This degree program combines cybersecurity with system and network administration. Classes focus on best practices in security, networking, operating systems, and their administration.\r\n\r\nOur students experience real-life scenarios through hands-on labs and simulations that are constructed based on feedback from the cybersecurity community and continually adapted to today\u0027s changing world. Not only do we teach security concepts and technologies, we also teach the information technology component that goes with it. Upon graduation, you will be ready to use your knowledge and skills in the workforce.\r\n\r\nThe curriculum contains core classes in computer hardware, Windows and Linux operating systems, networking, security, and programming. Students may have the opportunity to earn college credit for cooperative education or internship opportunities with local businesses.\r\n\r\nAll classes in the program can be taken online or on campus. We continue to evolve our advanced instructional methodologies to complement cutting-edge and remote learning technologies, providing our students with synchronous, virtual classroom environments. Our flexible, online and/or on-campus courses make it easier to achieve your educational goals. \r\n\r\nStudents can complete the program in two years, three years, or longer, depending upon prior preparation in Math, English, and Computers.",
15
- "fieldOfStudy": "CIP Code Data Pulled From Campus Solutions",
16
- "humanCode": null,
17
- "image": null,
18
- "inLanguage": null,
19
- "name": "Cybersecurity\u0026Computer Network",
20
- "otherIdentifier": null,
21
- "related": null,
22
- "resultDescription": [
23
- {
24
- "id": "urn:uuid:229c6a36-e66a-4096-b659-6258ef66ec02",
25
- "type": [
26
- "ResultDescription"
27
- ],
28
- "alignment": null,
29
- "allowedValue": null,
30
- "name": "Program Completed",
31
- "requiredLevel": null,
32
- "requiredValue": null,
33
- "resultType": "Status",
34
- "rubricCriterionLevel": null,
35
- "valueMax": null,
36
- "valueMin": null
37
- }
38
- ],
39
- "specialization": null,
40
- "tag": null,
41
- "version": null
42
- }
package/formkit.config.js DELETED
@@ -1,27 +0,0 @@
1
- import { generateClasses } from '@formkit/themes'
2
-
3
- const config = {
4
- config: {
5
- classes: generateClasses({
6
- global: { // classes
7
- outer: '$reset mb-4',
8
- input: 'form-control',
9
- label: 'form-label',
10
- help: 'form-text',
11
- message: 'invalid-feedback',
12
- },
13
- form: {
14
- form: "mt-1 mx-auto p-3 border rounded"
15
- },
16
- range: {
17
- input: '$reset form-range',
18
- },
19
- submit: {
20
- outer: '$reset mt-3',
21
- input: '$reset btn btn-primary'
22
- }
23
- })
24
- }
25
- }
26
-
27
- export default config
package/index-2.html DELETED
@@ -1,22 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <link rel="icon" href="/favicon.svg">
6
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
- <title>OB3 Achievement Definer</title>
8
- <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
9
- <script type="module" src="/src/main.js"></script>
10
- </head>
11
- <body>
12
- <div id="ob3-definer"
13
- data-achievement='{"id":"https://bismarckstate.edu/academics/programs/computersupport/","type":["Achievement"],"alignment":[],"achievementType":null,"creator":null,"creditsAvailable":null,"criteria":{"id":null,"narrative":"Test narrative"},"description":"This degree program combines cybersecurity with system and network administration. Classes focus on best practices in security, networking, operating systems, and their administration.\r\n\r\nOur students experience real-life scenarios through hands-on labs and simulations that are constructed based on feedback from the cybersecurity community and continually adapted to todays changing world. Not only do we teach security concepts and technologies, we also teach the information technology component that goes with it. Upon graduation, you will be ready to use your knowledge and skills in the workforce.\r\n\r\nThe curriculum contains core classes in computer hardware, Windows and Linux operating systems, networking, security, and programming. Students may have the opportunity to earn college credit for cooperative education or internship opportunities with local businesses.\r\n\r\nAll classes in the program can be taken online or on campus. We continue to evolve our advanced instructional methodologies to complement cutting-edge and remote learning technologies, providing our students with synchronous, virtual classroom environments. Our flexible, online and/or on-campus courses make it easier to achieve your educational goals. \r\n\r\nStudents can complete the program in two years, three years, or longer, depending upon prior preparation in Math, English, and Computers.","fieldOfStudy":"CIP Code Data Pulled From Campus Solutions","humanCode":null,"image":null,"inLanguage":null,"name":"Cybersecurity&Computer Network","otherIdentifier":null,"related":null,"resultDescription":[{"id":"urn:uuid:229c6a36-e66a-4096-b659-6258ef66ec02","type":["ResultDescription"],"alignment":null,"allowedValue":null,"name":"Program Completed","requiredLevel":null,"requiredValue":null,"resultType":"Status","rubricCriterionLevel":null,"valueMax":null,"valueMin":null}],"specialization":null,"tag":null,"version":null}'
14
- ></div>
15
- <script>
16
- document.addEventListener('DOMContentLoaded', () => {
17
- window.dispatchEvent(new CustomEvent('ob3-open'));
18
- });
19
- window.addEventListener('saveDefinition', function(e) {console.log(e);});
20
- </script>
21
- </body>
22
- </html>
package/index.html DELETED
@@ -1,19 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <link rel="icon" href="/favicon.svg">
6
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
- <title>OB3 Achievement Definer</title>
8
- <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
9
- <script type="module" src="/src/main.js"></script>
10
- </head>
11
- <body>
12
- <div id="ob3-definer"></div>
13
- <script>
14
- document.addEventListener('DOMContentLoaded', () => {
15
- window.dispatchEvent(new CustomEvent('ob3-open'));
16
- })
17
- </script>
18
- </body>
19
- </html>
package/jsconfig.json DELETED
@@ -1,8 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "paths": {
4
- "@/*": ["./src/*"]
5
- }
6
- },
7
- "exclude": ["node_modules", "dist"]
8
- }
@@ -1,7 +0,0 @@
1
- <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
- <g id="Layer_1">
3
- <g id="favicon" stroke-width="0"/>
4
- <path fill-rule="evenodd" clip-rule="evenodd" d="M10.586 5.062l7.876 7.878-9.063 9.156-3.39-3.387 9.063-9.157-2.977-2.977-5.502 4.086 3.993 4.007-1.414 1.415-4.007-4.008-1.472 1.374 5.502 4.086 2.836-2.838 3.888 3.89z" fill="#58B4AE"/>
5
- <path d="M3.324 18.981l3.389 3.389 9.787-9.924-3.39-3.388-9.786 9.923zM7.51 22.27l-2.977-2.977-.732.866 2.836 2.838 3.112-2.256-.473-.47-2.766 2.999.232.125.284-.125 1.382-.811z" fill="#95E1D3"/>
6
- </g>
7
- </svg>
package/src/App.vue DELETED
@@ -1,23 +0,0 @@
1
- <script setup>
2
- import AchievementDefiner from "@/components/AchievementDefiner.vue";
3
-
4
- const props = defineProps({
5
- achievement: {
6
- type: String,
7
- default: ""
8
- },
9
- submitText: {
10
- type: String,
11
- default: "Save"
12
- }
13
- });
14
- </script>
15
-
16
- <template>
17
- <div class="container">
18
- <AchievementDefiner :achievement="achievement" :submit-text="submitText"/>
19
- </div>
20
- </template>
21
-
22
- <style scoped>
23
- </style>
@@ -1,86 +0,0 @@
1
- /* color palette from <https://github.com/vuejs/theme> */
2
- :root {
3
- --vt-c-white: #ffffff;
4
- --vt-c-white-soft: #f8f8f8;
5
- --vt-c-white-mute: #f2f2f2;
6
-
7
- --vt-c-black: #181818;
8
- --vt-c-black-soft: #222222;
9
- --vt-c-black-mute: #282828;
10
-
11
- --vt-c-indigo: #2c3e50;
12
-
13
- --vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
14
- --vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
15
- --vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
16
- --vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
17
-
18
- --vt-c-text-light-1: var(--vt-c-indigo);
19
- --vt-c-text-light-2: rgba(60, 60, 60, 0.66);
20
- --vt-c-text-dark-1: var(--vt-c-white);
21
- --vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
22
- }
23
-
24
- /* semantic color variables for this project */
25
- :root {
26
- --color-background: var(--vt-c-white);
27
- --color-background-soft: var(--vt-c-white-soft);
28
- --color-background-mute: var(--vt-c-white-mute);
29
-
30
- --color-border: var(--vt-c-divider-light-2);
31
- --color-border-hover: var(--vt-c-divider-light-1);
32
-
33
- --color-heading: var(--vt-c-text-light-1);
34
- --color-text: var(--vt-c-text-light-1);
35
-
36
- --section-gap: 160px;
37
- }
38
-
39
- @media (prefers-color-scheme: dark) {
40
- :root {
41
- --color-background: var(--vt-c-black);
42
- --color-background-soft: var(--vt-c-black-soft);
43
- --color-background-mute: var(--vt-c-black-mute);
44
-
45
- --color-border: var(--vt-c-divider-dark-2);
46
- --color-border-hover: var(--vt-c-divider-dark-1);
47
-
48
- --color-heading: var(--vt-c-text-dark-1);
49
- --color-text: var(--vt-c-text-dark-2);
50
- }
51
- }
52
-
53
- *,
54
- *::before,
55
- *::after {
56
- box-sizing: border-box;
57
- margin: 0;
58
- font-weight: normal;
59
- }
60
-
61
- body {
62
- min-height: 100vh;
63
- color: var(--color-text);
64
- background: var(--color-background);
65
- transition:
66
- color 0.5s,
67
- background-color 0.5s;
68
- line-height: 1.6;
69
- font-family:
70
- Inter,
71
- -apple-system,
72
- BlinkMacSystemFont,
73
- 'Segoe UI',
74
- Roboto,
75
- Oxygen,
76
- Ubuntu,
77
- Cantarell,
78
- 'Fira Sans',
79
- 'Droid Sans',
80
- 'Helvetica Neue',
81
- sans-serif;
82
- font-size: 15px;
83
- text-rendering: optimizeLegibility;
84
- -webkit-font-smoothing: antialiased;
85
- -moz-osx-font-smoothing: grayscale;
86
- }
@@ -1,24 +0,0 @@
1
- /*
2
- @import './base.css';
3
- */
4
-
5
- #app {
6
- max-width: 1280px;
7
- margin: 0 auto;
8
- padding: 2rem;
9
- font-weight: normal;
10
- }
11
-
12
- @media (min-width: 1024px) {
13
- body {
14
- display: flex;
15
- width: 1024px;
16
- /*place-items: center;*/
17
- }
18
-
19
- #app {
20
- display: grid;
21
- grid-template-columns: 1fr 1fr;
22
- padding: 0 2rem;
23
- }
24
- }
@@ -1,33 +0,0 @@
1
- @import 'bootstrap/scss/bootstrap';
2
-
3
- [data-complete="true"]:not([data-empty="true"]) .form-control {
4
- //@extend .form-control, .is-valid;
5
- /*
6
- border-color: var(--bs-form-valid-border-color);
7
- padding-right: calc(1.5em + 0.75rem);
8
- background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");
9
- background-repeat: no-repeat;
10
- background-position: right calc(0.375em + 0.1875rem) center;
11
- background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
12
- */
13
- }
14
-
15
- /*
16
- [data-invalid="true"] .form-control {
17
- //@extend .form-control, .is-invalid;
18
- border-color: var(--bs-form-invalid-border-color);
19
- padding-right: calc(1.5em + 0.75rem);
20
- background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");
21
- background-repeat: no-repeat;
22
- background-position: right calc(0.375em + 0.1875rem) center;
23
- background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
24
- }
25
-
26
- [data-invalid="true"] .invalid-feedback {
27
- display: inline;
28
- width: 100%;
29
- margin-top: 0.25rem;
30
- font-size: 0.875em;
31
- color: var(--bs-form-invalid-color);
32
- }
33
- */
@@ -1,78 +0,0 @@
1
- <script setup>
2
- import {onMounted, ref, watch} from "vue";
3
- import MarkdownRenderer from "@/components/MarkdownRenderer.vue";
4
-
5
- const model = defineModel({ default: {
6
- }});
7
- const preview = ref('');
8
-
9
- watch(model, () => {
10
- preview.value = model.value.narrative;
11
- })
12
-
13
- onMounted(() => {
14
- preview.value = model.value.narrative ? model.value.narrative : '';
15
- })
16
-
17
- const criteriaTab = ref("edit");
18
-
19
- function selectTab(tab) {
20
- criteriaTab.value = tab;
21
- }
22
- </script>
23
-
24
- <template>
25
- <div class="formkit-wrapper required">
26
- <label class="form-label">Earning Criteria</label>
27
- </div>
28
-
29
- <ul class="nav nav-tabs" role="tablist">
30
- <li class="nav-item" role="presentation">
31
- <button class="nav-link" :class='{"active": criteriaTab === "edit"}' type="button" id="edit-tab" aria-controls="criteria-edit"
32
- role="tab" :aria-selected="criteriaTab === 'edit'" @click="selectTab('edit')">Edit
33
- </button>
34
- </li>
35
- <li class="nav-item" role="presentation">
36
- <button class="nav-link" :class='{"active": criteriaTab === "preview"}' type="button" id="preview-tab"
37
- aria-controls="criteria-preview" role="tab" :aria-selected="criteriaTab === 'preview'"
38
- @click="selectTab('preview')">Preview
39
- </button>
40
- </li>
41
- </ul>
42
-
43
- <div class="tab-content" id="criteria-tab">
44
- <div class="tab-pane fade" :class="{'show': (criteriaTab === 'edit'), 'active': (criteriaTab === 'edit')}"
45
- id="criteria-edit" role="tabpanel" aria-labelledby="edit-tab" v-show="criteriaTab === 'edit'">
46
- <FormKit type="group" name="criteria" v-model="model">
47
- <FormKit
48
- type="textarea"
49
- label="Narrative"
50
- name="narrative"
51
- rows="5"
52
- validation="require_one:id"
53
- help="A narrative of what is needed to earn the credential. Markdown is allowed."
54
- />
55
-
56
- <FormKit
57
- type="text"
58
- label="Criteria URL"
59
- name="id"
60
- validation="require_one:narrative|url"
61
- help="The URL of a webpage that describes in a human-readable format the criteria for the credential."
62
- />
63
- </FormKit>
64
- </div>
65
-
66
- <div class="tab-pane fade" :class="{'show': (criteriaTab === 'preview'), 'active': (criteriaTab === 'preview')}"
67
- id="criteria-preview" role="tabpanel" aria-labelledby="preview-tab" v-show="criteriaTab === 'preview'">
68
- <label class="mt-3 mb-2">Narrative Preview</label>
69
- <MarkdownRenderer :source="preview" class="border rounded p-3"/>
70
- </div>
71
- </div>
72
- </template>
73
-
74
- <style scoped>
75
- #criteria-preview {
76
- min-height: 302px;
77
- }
78
- </style>
@@ -1,223 +0,0 @@
1
- <script setup>
2
- import {onBeforeMount, reactive, ref, watch} from "vue";
3
- import BasicTab from "@/components/BasicTab.vue";
4
- import DetailTab from "@/components/DetailTab.vue";
5
- import AlignmentsTab from "@/components/AlignmentsTab.vue";
6
- import AdditionalTab from "@/components/AdditionalTab.vue";
7
-
8
- const props = defineProps({
9
- achievement: {
10
- type: String,
11
- default: ""
12
- },
13
- submitText: {
14
- type: String,
15
- default: "Save"
16
- }
17
- });
18
- const emit = defineEmits(['saveDefinition']);
19
-
20
- const tab = ref("basic");
21
- const submitted = ref(false);
22
- const form = ref(null);
23
-
24
- const formData = reactive({
25
- type: ['Achievement'],
26
- basic: {},
27
- detail: {},
28
- alignments: {},
29
- additional: {},
30
- });
31
-
32
- const achievementData = reactive({});
33
-
34
- onBeforeMount(() => {
35
- try {
36
- const achievement = JSON.parse(JSON.stringify(JSON.parse(props.achievement), (key, value) => {
37
- return value == null ? undefined : value;
38
- }));
39
-
40
- formData.basic = {
41
- name: achievement.name || '',
42
- achievementType: achievement.achievementType || null,
43
- image: achievement.image || {},
44
- description: achievement.description || '',
45
- criteria: achievement.criteria || {},
46
- };
47
- formData.detail = {
48
- humanCode: achievement.humanCode || null,
49
- inLanguage: achievement.inLanguage || null,
50
- version: achievement.version || null,
51
- creditsAvailable: achievement.creditsAvailable || null,
52
- specialization: achievement.specialization || null,
53
- fieldOfStudy: achievement.fieldOfStudy || null,
54
- };
55
- formData.alignments = {
56
- resultDescription: achievement.resultDescription || [],
57
- alignment: achievement.alignment || [],
58
- };
59
- formData.additional = {
60
- id: achievement.id || null,
61
- tag: achievement.tag || [],
62
- related: achievement.related || [],
63
- otherIdentifier: achievement.otherIdentifier || [],
64
- creator: achievement.creator || {},
65
- };
66
- /*
67
- achievementData.basic = {...achievement.basic };
68
- achievementData.detail = {...achievement.detail };
69
- achievementData.alignments = {...achievement.alignments };
70
- achievementData.additional = {...achievement.additional };
71
- */
72
- } catch (e) {
73
- // No valid OB3 Achievement definition was passed
74
- console.log('Error parsing passed achievement: ', e);
75
- }
76
- });
77
-
78
- watch(formData, (value) => {
79
- Object.assign(achievementData, value.basic);
80
- Object.assign(achievementData, value.detail);
81
- Object.assign(achievementData, value.alignments);
82
- Object.assign(achievementData, value.additional);
83
- });
84
-
85
- function selectTab(selected) {
86
- tab.value = selected;
87
- }
88
-
89
- function removeEmpty(obj) {
90
- /*
91
- return Object.fromEntries(
92
- Object.entries(obj)
93
- .filter(([_, v]) => v != null && v !== '' && (!Array.isArray(v) || v.length > 0))
94
- .map(([k, v]) => [k, (v === Object(v) && !Array.isArray(v)) ? removeEmpty(v) : v])
95
- );
96
- */
97
- return JSON.parse(JSON.stringify(obj, (key, value) => {
98
- return ((value === null || value === '' || (Array.isArray(value) && value.length === 0)) ? undefined : value);
99
- }));
100
- }
101
-
102
- function save(formData) {
103
- if (!formData.image.id) {
104
- formData.image = null;
105
- }
106
- //console.log('formData', formData);
107
- const cleaned = removeEmpty(formData);
108
- //emit('saveDefinition', cleaned);
109
- const formEl = document.getElementById(form.value.node.props.id);
110
- formEl.dispatchEvent(new CustomEvent('saveDefinition', { bubbles: true, detail: JSON.stringify(cleaned) }));
111
- //console.log('Submitting', cleaned);
112
- }
113
-
114
- function sendChange(formData) {
115
- try {
116
- const formEl = document.getElementById(form.value.node.props.id);
117
- const valid = form.value.node.context.state.valid;
118
- const cleaned = removeEmpty(form.value.node.value);
119
- formEl.dispatchEvent(new CustomEvent('changed', {bubbles: true, detail: JSON.stringify(cleaned)}));
120
- if (valid) {
121
- formEl.dispatchEvent(new CustomEvent('updated', {bubbles: true, detail: JSON.stringify(cleaned)}));
122
- }
123
- } catch (e) {}
124
- }
125
-
126
- function showErrors(node) {
127
- submitted.value = true;
128
- }
129
- </script>
130
-
131
- <template>
132
- <div>
133
- <ul class="nav nav-tabs" role="tablist">
134
- <li class="nav-item" role="presentation">
135
- <button class="nav-link" :class='{"active": (tab === "basic")}' type="button" id="basic-tab" role="tab" aria-controls="tab-basic" :aria-selected="(tab === 'basic') ? 'true' : 'false'" @click="selectTab('basic')">Primary Details</button>
136
- </li>
137
- <li class="nav-item" role="presentation">
138
- <button class="nav-link" :class='{"active": (tab === "detail")}' type="button" id="detail-tab" role="tab" aria-controls="tab-detail" :aria-selected="(tab === 'detail') ? 'true' : 'false'" @click="selectTab('detail')">Additional Details</button>
139
- </li>
140
- <li class="nav-item" role="presentation">
141
- <button class="nav-link" :class='{"active": (tab === "alignments")}' type="button" id="detail-tab" role="tab" aria-controls="tab-alignments" :aria-selected="(tab === 'alignments') ? 'true' : 'false'" @click="selectTab('alignments')">Alignments</button>
142
- </li>
143
- <li class="nav-item" role="presentation">
144
- <button class="nav-link" :class='{"active": (tab === "additional")}' type="button" id="additional-tab" role="tab" aria-controls="tab-additional" :aria-selected="(tab === 'additional') ? 'true' : 'false'" @click="selectTab('additional')">Additional Information</button>
145
- </li>
146
- </ul>
147
-
148
- <div class="tab-content mt-3" id="tab-content">
149
- <FormKit
150
- type="form"
151
- :actions="false"
152
- ref="form"
153
- @submit="save"
154
- @submit-invalid="showErrors"
155
- @change="sendChange"
156
- #default="{ state: { valid } }"
157
- validation-visibility="live"
158
- >
159
- <div class="alert alert-warning" role="alert" v-if="!valid && submitted">
160
- There are some errors in the form submission. Please correct the errors and then resubmit the form.
161
- </div>
162
-
163
- <FormKit
164
- type="hidden"
165
- name="@context"
166
- :value="[ 'https://www.w3.org/2018/credentials/v2', 'https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.3.json' ]"
167
- />
168
-
169
- <FormKit
170
- type="hidden"
171
- name="type"
172
- v-model="formData.type"
173
- :value="[ 'Achievement' ]"
174
- />
175
-
176
- <BasicTab
177
- v-model="formData.basic"
178
- v-show="tab === 'basic'"
179
- />
180
- <DetailTab
181
- v-model="formData.detail"
182
- v-show="tab === 'detail'"
183
- />
184
- <AlignmentsTab
185
- v-model="formData.alignments"
186
- v-show="tab === 'alignments'"
187
- />
188
- <AdditionalTab
189
- v-model="formData.additional"
190
- v-show="tab === 'additional'"
191
- />
192
-
193
- <button class="btn btn-primary float-end mt-5" type="submit" :disabled="false">{{ submitText }}</button>
194
- </FormKit>
195
- </div>
196
- </div>
197
- </template>
198
-
199
- <style>
200
- .formkit-wrapper.required .form-label:before{
201
- color: red;
202
- content: "*";
203
- position: absolute;
204
- margin-left: -10px;
205
- }
206
-
207
- [data-invalid="true"] .form-control {
208
- border-color: #dc3545;
209
- padding-right: calc(1.5em + 0.75rem);
210
- background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");
211
- background-repeat: no-repeat;
212
- background-position: right calc(0.375em + 0.1875rem) center;
213
- background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
214
- }
215
-
216
- [data-invalid="true"] .invalid-feedback {
217
- display: inline;
218
- width: 100%;
219
- margin-top: 0.25rem;
220
- font-size: 0.875em;
221
- color: #dc3545;
222
- }
223
- </style>