@vue-skuilder/edit-ui 0.1.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 (28) hide show
  1. package/dist/assets/index.css +1 -0
  2. package/dist/edit-ui.es.js +71090 -0
  3. package/dist/edit-ui.es.js.map +1 -0
  4. package/dist/edit-ui.umd.js +83 -0
  5. package/dist/edit-ui.umd.js.map +1 -0
  6. package/package.json +67 -0
  7. package/src/components/BulkImport/CardPreviewList.vue +345 -0
  8. package/src/components/BulkImportView.vue +633 -0
  9. package/src/components/CourseEditor.vue +164 -0
  10. package/src/components/ViewableDataInputForm/DataInputForm.vue +533 -0
  11. package/src/components/ViewableDataInputForm/FieldInput.types.ts +33 -0
  12. package/src/components/ViewableDataInputForm/FieldInputs/AudioInput.vue +188 -0
  13. package/src/components/ViewableDataInputForm/FieldInputs/ChessPuzzleInput.vue +79 -0
  14. package/src/components/ViewableDataInputForm/FieldInputs/FieldInput.css +12 -0
  15. package/src/components/ViewableDataInputForm/FieldInputs/ImageInput.vue +231 -0
  16. package/src/components/ViewableDataInputForm/FieldInputs/IntegerInput.vue +49 -0
  17. package/src/components/ViewableDataInputForm/FieldInputs/MarkdownInput.vue +34 -0
  18. package/src/components/ViewableDataInputForm/FieldInputs/MediaDragDropUploader.vue +246 -0
  19. package/src/components/ViewableDataInputForm/FieldInputs/MidiInput.vue +113 -0
  20. package/src/components/ViewableDataInputForm/FieldInputs/NumberInput.vue +49 -0
  21. package/src/components/ViewableDataInputForm/FieldInputs/OptionsFieldInput.ts +161 -0
  22. package/src/components/ViewableDataInputForm/FieldInputs/StringInput.vue +49 -0
  23. package/src/components/ViewableDataInputForm/FieldInputs/typeValidators.ts +49 -0
  24. package/src/components/index.ts +21 -0
  25. package/src/index.ts +6 -0
  26. package/src/stores/useDataInputFormStore.ts +49 -0
  27. package/src/stores/useFieldInputStore.ts +191 -0
  28. package/src/vue-shims.d.ts +5 -0
@@ -0,0 +1,246 @@
1
+ <template>
2
+ <div class="mr-2 mb-2">
3
+ <v-label class="text-h5">Add media:</v-label>
4
+ <div
5
+ class="drop-zone"
6
+ :class="{ 'drop-zone--over': isDragging }"
7
+ @drop="dropHandler"
8
+ @dragover.prevent="dragOverHandler"
9
+ @dragenter.prevent="dragEnterHandler"
10
+ @dragleave.prevent="dragLeaveHandler"
11
+ >
12
+ <input
13
+ ref="fileInput"
14
+ type="file"
15
+ accept="image/*,audio/*"
16
+ multiple
17
+ style="display: none"
18
+ @change="handleFileInput"
19
+ />
20
+ <!-- <template> -->
21
+ <div v-for="(item, index) in mediaItems" :key="index" class="media-item">
22
+ <template v-if="item.type === 'image'">
23
+ <img :src="item.thumbnailUrl" alt="Uploaded image thumbnail" class="thumbnail" />
24
+ </template>
25
+ <template v-else-if="item.type === 'audio'">
26
+ <audio controls :src="item.url"></audio>
27
+ </template>
28
+ <v-btn size="small" @click="removeMedia(index)">Remove</v-btn>
29
+ </div>
30
+ <!-- <template> -->
31
+ Drop image or audio files here...
32
+ <v-btn @click="triggerFileInput">Or Click to Upload</v-btn>
33
+ <!-- </template> -->
34
+ <!-- <v-btn @click="addMoreMedia">Add More Media</v-btn> -->
35
+ <!-- </template> -->
36
+ </div>
37
+ </div>
38
+ </template>
39
+
40
+ <script lang="ts">
41
+ import { defineComponent } from 'vue';
42
+ import FieldInput from './OptionsFieldInput';
43
+ import { Status } from '@vue-skuilder/common';
44
+ import { FieldInputSetupReturn } from './OptionsFieldInput';
45
+
46
+ export interface MediaItem {
47
+ type: 'image' | 'audio';
48
+ file: File;
49
+ url: string;
50
+ thumbnailUrl?: string;
51
+ }
52
+
53
+ export default defineComponent({
54
+ name: 'MediaDragDropUploader',
55
+ extends: FieldInput,
56
+
57
+ setup(props, ctx) {
58
+ // Get the parent setup result
59
+ const parentSetup = FieldInput.setup?.(props, ctx) as FieldInputSetupReturn;
60
+
61
+ // Now you can access fieldStore and other parent setup properties
62
+ const { fieldStore } = parentSetup;
63
+
64
+ // Return both parent and child setup properties
65
+ return {
66
+ ...parentSetup,
67
+ fieldStore,
68
+ // Add any additional setup properties specific to this component
69
+ };
70
+ },
71
+
72
+ data() {
73
+ return {
74
+ isDragging: false,
75
+ mediaItems: [] as MediaItem[],
76
+ };
77
+ },
78
+
79
+ computed: {
80
+ hasMedia(): boolean {
81
+ return this.mediaItems.length > 0;
82
+ },
83
+ },
84
+
85
+ created() {
86
+ // this.validate();
87
+ },
88
+
89
+ methods: {
90
+ dragOverHandler(event: DragEvent) {
91
+ event.preventDefault();
92
+ },
93
+
94
+ dragEnterHandler(event: DragEvent) {
95
+ event.preventDefault();
96
+ this.isDragging = true;
97
+ },
98
+
99
+ dragLeaveHandler(event: DragEvent) {
100
+ event.preventDefault();
101
+ this.isDragging = false;
102
+ },
103
+
104
+ dropHandler(event: DragEvent) {
105
+ event.preventDefault();
106
+ this.isDragging = false;
107
+ const files = event.dataTransfer?.files;
108
+ if (files) {
109
+ this.processFiles(files);
110
+ }
111
+ },
112
+
113
+ triggerFileInput() {
114
+ (this.$refs.fileInput as HTMLInputElement).click();
115
+ },
116
+
117
+ handleFileInput(event: Event) {
118
+ const files = (event.target as HTMLInputElement).files;
119
+ if (files) {
120
+ this.processFiles(files);
121
+ }
122
+ },
123
+
124
+ processFiles(files: FileList) {
125
+ Array.from(files).forEach((file) => {
126
+ this.addMediaItem(file);
127
+ });
128
+ this.updateStore();
129
+ },
130
+
131
+ addMediaItem(file: File) {
132
+ const type = file.type.startsWith('image/') ? 'image' : 'audio';
133
+ const item: MediaItem = {
134
+ type,
135
+ file,
136
+ url: URL.createObjectURL(file),
137
+ };
138
+
139
+ if (type === 'image') {
140
+ this.createThumbnail(file).then((thumbnailUrl) => {
141
+ item.thumbnailUrl = thumbnailUrl;
142
+ this.$nextTick(() => {
143
+ this.$forceUpdate();
144
+ });
145
+ });
146
+ }
147
+
148
+ this.mediaItems.push(item);
149
+ },
150
+
151
+ async createThumbnail(file: File): Promise<string> {
152
+ return new Promise((resolve) => {
153
+ const reader = new FileReader();
154
+ reader.onload = (e: ProgressEvent<FileReader>) => {
155
+ resolve(e.target?.result as string);
156
+ };
157
+ reader.readAsDataURL(file);
158
+ });
159
+ },
160
+
161
+ removeMedia(index: number) {
162
+ URL.revokeObjectURL(this.mediaItems[index].url);
163
+ this.mediaItems.splice(index, 1);
164
+ this.updateStore();
165
+ },
166
+
167
+ clearData() {
168
+ this.mediaItems.forEach((item) => {
169
+ URL.revokeObjectURL(item.url);
170
+ });
171
+ this.mediaItems = [];
172
+ this.updateStore();
173
+ // this.validate();
174
+ },
175
+
176
+ addMoreMedia() {
177
+ console.log('addMoreMedia');
178
+ this.triggerFileInput();
179
+ },
180
+
181
+ updateStore() {
182
+ // for (let i = 1; i <= 10; i++) {
183
+ // delete this.dataInputForm.dataInputForm[`image-${i}`];
184
+ // delete this.dataInputForm.dataInputForm[`audio-${i}`];
185
+ // }
186
+
187
+ let imageCount = 0;
188
+ let audioCount = 0;
189
+ this.mediaItems.forEach((item) => {
190
+ if (item.type === 'image') {
191
+ imageCount++;
192
+ this.fieldStore.setMedia(`image-${imageCount}`, {
193
+ content_type: item.file.type,
194
+ data: item.file,
195
+ });
196
+ } else if (item.type === 'audio') {
197
+ audioCount++;
198
+ this.fieldStore.setMedia(`audio-${audioCount}`, {
199
+ content_type: item.file.type,
200
+ data: item.file,
201
+ });
202
+ }
203
+ });
204
+ // this.validate();
205
+ },
206
+
207
+ getValidators() {
208
+ return [
209
+ () => ({
210
+ status: this.mediaItems.length > 0 ? Status.ok : Status.error,
211
+ msg: this.mediaItems.length > 0 ? '' : 'At least one media item is required',
212
+ }),
213
+ ];
214
+ },
215
+ },
216
+ });
217
+ </script>
218
+
219
+ <style scoped>
220
+ @import './FieldInput.css';
221
+
222
+ .drop-zone {
223
+ border: 2px dashed #ccc;
224
+ border-radius: 4px;
225
+ padding: 20px;
226
+ text-align: center;
227
+ transition: all 0.3s ease;
228
+ }
229
+
230
+ .drop-zone--over {
231
+ border-color: #000;
232
+ background-color: rgba(0, 0, 0, 0.1);
233
+ }
234
+
235
+ .thumbnail {
236
+ max-width: 100px;
237
+ max-height: 100px;
238
+ margin-right: 10px;
239
+ }
240
+
241
+ .media-item {
242
+ display: flex;
243
+ align-items: center;
244
+ margin-bottom: 10px;
245
+ }
246
+ </style>
@@ -0,0 +1,113 @@
1
+ <template>
2
+ <div>
3
+ <div v-if="recording">
4
+ <span class="text-h5">
5
+ Now Recording from device:
6
+ <span class="font-weight-black">{{ midi.configuredInput }}</span>
7
+ </span>
8
+ </div>
9
+ <syllable-seq-vis v-if="true" ref="inputVis" :seq="SylSeq" last-t-ssuggestion="5000" />
10
+ <v-btn color="primary" :disabled="hasRecording()" @click="play">
11
+ Preview
12
+ <v-icon end>volume_up</v-icon>
13
+ </v-btn>
14
+ <v-btn color="error" :disabled="hasRecording()" @click="reset">
15
+ Clear and try again
16
+ <v-icon end>close</v-icon>
17
+ </v-btn>
18
+ <v-checkbox v-model="transpositions" label="Include Transpositions" @click.capture="reset"></v-checkbox>
19
+ </div>
20
+ </template>
21
+
22
+ <script lang="ts">
23
+ import { defineComponent } from 'vue';
24
+ import FieldInput from './OptionsFieldInput';
25
+ import {
26
+ SkMidi,
27
+ eventsToSyllableSequence,
28
+ SyllableSequence,
29
+ transposeSyllableSeq,
30
+ SyllableSeqVis,
31
+ } from '@vue-skuilder/courses';
32
+
33
+ export default defineComponent({
34
+ name: 'MidiInput',
35
+ components: {
36
+ SyllableSeqVis,
37
+ },
38
+ extends: FieldInput,
39
+
40
+ data() {
41
+ return {
42
+ midi: null as SkMidi | null,
43
+ recording: false,
44
+ SylSeq: eventsToSyllableSequence([]) as SyllableSequence,
45
+ display: false,
46
+ transpositions: false,
47
+ };
48
+ },
49
+
50
+ async created() {
51
+ try {
52
+ this.midi = await SkMidi.instance();
53
+ this.record();
54
+ // [ ] this wasn't updated when the dataInputFormStore / fieldInputStore components were created ?
55
+ this.store[this.field.name] = this.getTransposedSeqs;
56
+ } catch (e) {
57
+ throw new Error(`MidiInput.created: ${e}`);
58
+ }
59
+ },
60
+
61
+ methods: {
62
+ record() {
63
+ if (!this.midi) return;
64
+ this.midi.record();
65
+ this.midi.addNoteonListenter((e) => {
66
+ this.SylSeq.append(e);
67
+ (this.$refs.inputVis as InstanceType<typeof SyllableSeqVis>).updateBounds();
68
+ });
69
+ this.recording = true;
70
+ },
71
+
72
+ getTransposedSeqs() {
73
+ if (!this.midi) return [];
74
+ if (this.transpositions) {
75
+ return [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6].map((shift) => {
76
+ return transposeSyllableSeq(this.midi!.recording, shift);
77
+ });
78
+ } else {
79
+ return [this.midi.recording];
80
+ }
81
+ },
82
+
83
+ clearData() {
84
+ if (!this.midi) return;
85
+ console.log('midiInput clearing data...');
86
+ this.midi.stopRecording();
87
+ this.midi.eraseRecording();
88
+ this.SylSeq = eventsToSyllableSequence([]);
89
+ this.record();
90
+ this.recording = true;
91
+
92
+ this.store.convertedInput[this.field.name] = this.midi.recording;
93
+ this.store.validation[this.field.name] = false;
94
+ this.store[this.field.name] = this.getTransposedSeqs;
95
+ },
96
+
97
+ hasRecording(): boolean {
98
+ return this.midi?.hasRecording ?? false;
99
+ },
100
+
101
+ reset() {
102
+ this.clearData();
103
+ },
104
+
105
+ play() {
106
+ if (!this.midi) return;
107
+ this.midi.play();
108
+ this.display = true;
109
+ this.validate();
110
+ },
111
+ },
112
+ });
113
+ </script>
@@ -0,0 +1,49 @@
1
+ <template>
2
+ <v-text-field
3
+ ref="inputField"
4
+ v-model="modelValue"
5
+ variant="filled"
6
+ type="number"
7
+ :name="field.name"
8
+ :label="field.name"
9
+ :rules="vuetifyRules()"
10
+ :hint="validationStatus.msg"
11
+ :autofocus="autofocus"
12
+ />
13
+ </template>
14
+
15
+ <script lang="ts">
16
+ import { defineComponent, computed } from 'vue';
17
+ import { numberValidator } from './typeValidators';
18
+ import FieldInput from './OptionsFieldInput';
19
+ import { ValidatingFunction } from '@vue-skuilder/common';
20
+
21
+ export default defineComponent({
22
+ name: 'NumberInput',
23
+ extends: FieldInput,
24
+
25
+ setup(props, ctx) {
26
+ // Get all the setup logic from parent
27
+ const parentSetup = FieldInput.setup?.(props, ctx);
28
+
29
+ const validators = computed<ValidatingFunction[]>(() => {
30
+ const baseValidators = FieldInput.validators.call(this);
31
+
32
+ if (props.field.validator?.test) {
33
+ baseValidators.push(props.field.validator.test);
34
+ }
35
+
36
+ if (baseValidators) {
37
+ return [numberValidator, ...baseValidators];
38
+ } else {
39
+ return [numberValidator];
40
+ }
41
+ });
42
+
43
+ return {
44
+ ...parentSetup,
45
+ validators,
46
+ };
47
+ },
48
+ });
49
+ </script>
@@ -0,0 +1,161 @@
1
+ import {
2
+ computed,
3
+ ComputedRef,
4
+ defineComponent,
5
+ PropType,
6
+ Ref,
7
+ ref,
8
+ watch,
9
+ WritableComputedRef,
10
+ } from 'vue';
11
+ import {
12
+ ValidatingFunction,
13
+ FieldDefinition,
14
+ ValidationResult,
15
+ validationFunctionToVuetifyRule,
16
+ VuetifyRule,
17
+ Status,
18
+ CourseElo,
19
+ } from '@vue-skuilder/common';
20
+ import { useFieldInputStore } from '../../../stores/useFieldInputStore';
21
+
22
+ export interface FieldInputSetupReturn {
23
+ inputField: Ref<HTMLInputElement | null>;
24
+ fieldStore: ReturnType<typeof useFieldInputStore>;
25
+ modelValue: WritableComputedRef<unknown>;
26
+ validators: ComputedRef<ValidatingFunction[]>;
27
+ focus: () => void;
28
+ userInput: () => unknown;
29
+ clearData: () => void;
30
+ setData: (data: unknown) => void;
31
+ vuetifyRules: () => VuetifyRule[];
32
+ generateTags: () => string[];
33
+ generateELO: () => CourseElo | undefined;
34
+ }
35
+
36
+ export default defineComponent({
37
+ name: 'FieldInput',
38
+
39
+ props: {
40
+ autofocus: Boolean,
41
+ field: {
42
+ type: Object as PropType<FieldDefinition>,
43
+ required: true,
44
+ },
45
+ },
46
+ setup(props): FieldInputSetupReturn {
47
+ const fieldStore = useFieldInputStore();
48
+ const inputField = ref<HTMLInputElement | null>(null);
49
+ // [ ] TODO: Implement hint - need richer validation result
50
+ // on the fieldStore to do this.
51
+ //
52
+ // Exose / Retrieve it as a computed property
53
+ //
54
+ // const hint = ref('');
55
+
56
+ // Computed property for v-model binding in child components
57
+ const modelValue = computed({
58
+ get: () => fieldStore.inputs[props.field.name],
59
+ set: (value) => fieldStore.setFieldValue(props.field.name, value),
60
+ });
61
+
62
+ watch(modelValue, (newValue: unknown, oldValue: unknown) => {
63
+ console.log('[FieldInput] modelValue changed:', {
64
+ new: newValue,
65
+ old: oldValue,
66
+ currentStoreValue: fieldStore.inputs[props.field.name],
67
+ });
68
+ // Trigger validation when value changes
69
+ // props.uiValidationFunction();
70
+ //
71
+ // validate();
72
+ // fieldStore.setFieldValue(props.field.name, newValue);
73
+ });
74
+
75
+ const validators = computed(() => {
76
+ const ret = [];
77
+ if (props.field?.validator) {
78
+ ret.push(props.field.validator.test);
79
+ }
80
+ return ret;
81
+ });
82
+
83
+ const focus = () => {
84
+ if (inputField.value) {
85
+ inputField.value.focus();
86
+ }
87
+ };
88
+
89
+ const userInput = () => {
90
+ if (!props.field?.name) {
91
+ throw new Error('Field name is required for FieldInput component');
92
+ }
93
+ return fieldStore.inputs[props.field.name];
94
+ };
95
+
96
+ const setData = (data: unknown) => {
97
+ if (!props.field?.name) {
98
+ throw new Error('Field name is required for FieldInput component');
99
+ }
100
+ fieldStore.setFieldValue(props.field.name, data);
101
+ };
102
+
103
+ const clearData = () => {
104
+ console.log(
105
+ `[FieldInput] Running generic clearData() for ${props.field?.name} in FieldInput.ts`
106
+ );
107
+ if (inputField.value) {
108
+ // if (inputField.value.type === 'file') {
109
+ inputField.value.value = '';
110
+ // } else if (inputField.value.type === 'text') {
111
+ // inputField.value.value = '';
112
+ // } else if (inputField.value.type === '')
113
+ if (!props.field?.name) return;
114
+
115
+ fieldStore.clearField(props.field.name);
116
+ }
117
+ };
118
+
119
+ const vuetifyRules = () => {
120
+ if (props.field?.validator) {
121
+ return validators.value.map((f) => {
122
+ return validationFunctionToVuetifyRule(f);
123
+ });
124
+ }
125
+ return [];
126
+ };
127
+
128
+ const generateTags = () => {
129
+ console.log('[FieldInput] Running generic generateTags() in FieldInput.ts');
130
+ return props.field?.tagger ? props.field.tagger(userInput()) : [];
131
+ };
132
+
133
+ const generateELO = () => {
134
+ console.log('[FieldInput] Running generic generateELO() in FieldInput.ts');
135
+ return props.field?.generateELO ? props.field.generateELO(userInput()) : undefined;
136
+ };
137
+
138
+ return {
139
+ inputField,
140
+ fieldStore,
141
+ modelValue,
142
+ validators,
143
+ focus,
144
+ userInput,
145
+ clearData,
146
+ setData,
147
+ vuetifyRules,
148
+ generateTags,
149
+ generateELO,
150
+ };
151
+ },
152
+
153
+ data() {
154
+ return {
155
+ validationStatus: {
156
+ status: Status.ok,
157
+ msg: '',
158
+ } as ValidationResult,
159
+ };
160
+ },
161
+ });
@@ -0,0 +1,49 @@
1
+ <template>
2
+ <v-text-field
3
+ ref="inputField"
4
+ v-model="modelValue"
5
+ variant="filled"
6
+ type="text"
7
+ :name="field.name"
8
+ :label="field.name"
9
+ :rules="vuetifyRules()"
10
+ :hint="validationStatus.msg"
11
+ :autofocus="autofocus"
12
+ />
13
+ </template>
14
+
15
+ <script lang="ts">
16
+ import { defineComponent, computed } from 'vue';
17
+ import FieldInput from './OptionsFieldInput';
18
+ import { ValidatingFunction } from '../../../../base-course/Interfaces/ValidatingFunction';
19
+
20
+ export default defineComponent({
21
+ name: 'StringInput',
22
+ extends: FieldInput,
23
+
24
+ setup(props, ctx) {
25
+ // Get all the setup logic from parent
26
+ const parentSetup = FieldInput.setup?.(props, ctx);
27
+
28
+ // [ ] Test datashape-field-custom validators
29
+ const validators = computed<ValidatingFunction[]>(() => {
30
+ const baseValidators = FieldInput.validators.call(this);
31
+
32
+ if (props.field.validator?.test) {
33
+ baseValidators.push(props.field.validator.test);
34
+ }
35
+
36
+ if (baseValidators) {
37
+ return baseValidators;
38
+ } else {
39
+ return [];
40
+ }
41
+ });
42
+
43
+ return {
44
+ ...parentSetup,
45
+ validators,
46
+ };
47
+ },
48
+ });
49
+ </script>
@@ -0,0 +1,49 @@
1
+ import { Status } from '@vue-skuilder/common';
2
+ import { ValidationResult } from '@vue-skuilder/common';
3
+
4
+ const okResult: ValidationResult = {
5
+ status: Status.ok,
6
+ msg: '',
7
+ };
8
+
9
+ export function numberValidator(value: string): ValidationResult {
10
+ const errorResult: ValidationResult = {
11
+ status: Status.error,
12
+ msg: 'This input must be a number',
13
+ };
14
+
15
+ if (isNumeric(value)) {
16
+ return okResult;
17
+ } else {
18
+ return errorResult;
19
+ }
20
+ }
21
+
22
+ export function integerValidator(value: string): ValidationResult {
23
+ const errorResult: ValidationResult = {
24
+ status: Status.error,
25
+ msg: 'This input must be an integer (..., -2, -1, 0, 1, 2, ...).',
26
+ };
27
+
28
+ if (numberValidator(value).status === Status.error) {
29
+ return numberValidator(value);
30
+ }
31
+
32
+ if (isInteger(value)) {
33
+ return okResult;
34
+ } else {
35
+ return errorResult;
36
+ }
37
+ }
38
+
39
+ function isNumeric(value: unknown): boolean {
40
+ // pilfered from Angular and assumed to be correctish:
41
+ // https://github.com/angular/angular/blob/4.3.x/packages/common/src/pipes/number_pipe.ts#L172
42
+
43
+ // @ts-expect-error - see above
44
+ return !isNaN(value - parseFloat(value));
45
+ }
46
+
47
+ function isInteger(value: string) {
48
+ return /^[+,-]?\s?\d+$/.test(value);
49
+ }
@@ -0,0 +1,21 @@
1
+ // Edit UI Components - Core editing components migrated from platform-ui
2
+
3
+ export { default as CourseEditor } from './CourseEditor.vue';
4
+ export { default as DataInputForm } from './ViewableDataInputForm/DataInputForm.vue';
5
+ export { default as BulkImportView } from './BulkImportView.vue';
6
+
7
+ // Field Input Components
8
+ export { default as StringInput } from './ViewableDataInputForm/FieldInputs/StringInput.vue';
9
+ export { default as MarkdownInput } from './ViewableDataInputForm/FieldInputs/MarkdownInput.vue';
10
+ export { default as NumberInput } from './ViewableDataInputForm/FieldInputs/NumberInput.vue';
11
+ export { default as IntegerInput } from './ViewableDataInputForm/FieldInputs/IntegerInput.vue';
12
+ export { default as MediaDragDropUploader } from './ViewableDataInputForm/FieldInputs/MediaDragDropUploader.vue';
13
+ export { default as ChessPuzzleInput } from './ViewableDataInputForm/FieldInputs/ChessPuzzleInput.vue';
14
+ export { default as MidiInput } from './ViewableDataInputForm/FieldInputs/MidiInput.vue';
15
+ // export { default as AudioInput } from './ViewableDataInputForm/FieldInputs/AudioInput.vue'; // Commented out - file is deprecated
16
+ // export { default as ImageInput } from './ViewableDataInputForm/FieldInputs/ImageInput.vue'; // Commented out - file is deprecated
17
+
18
+ // Field Input Base and Utilities
19
+ export { default as OptionsFieldInput } from './ViewableDataInputForm/FieldInputs/OptionsFieldInput';
20
+ export * from './ViewableDataInputForm/FieldInputs/typeValidators';
21
+ export * from './ViewableDataInputForm/FieldInput.types';
package/src/index.ts ADDED
@@ -0,0 +1,6 @@
1
+ // Edit UI Library - Vue Component Library for Course Editing
2
+ export * from './components';
3
+
4
+ // Stores
5
+ export { useDataInputFormStore } from './stores/useDataInputFormStore';
6
+ export { useFieldInputStore } from './stores/useFieldInputStore';