Skip to content

Commit b33d052

Browse files
committed
"working" render of fallingLetters component.
1 parent 75e3b8f commit b33d052

File tree

7 files changed

+207
-116
lines changed

7 files changed

+207
-116
lines changed

docs/.vitepress/config.mts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ export default defineConfig({
1212
},
1313
},
1414
optimizeDeps: {
15-
exclude: ['@vue-skuilder/courseware', '@vue-skuilder/common-ui', '@vue-skuilder/platform-ui'],
15+
exclude: ['@vue-skuilder/courseware', '@vue-skuilder/common-ui', '@vue-skuilder/platform-ui', '@vue-skuilder/db'],
16+
},
17+
ssr: {
18+
noExternal: ['@vojtechlanka/vue-tags-input'],
1619
},
1720
},
1821
title: 'skuilder',

docs/cards.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,24 @@
11
# Cards
22

3-
<script setup>
3+
Test page works.
4+
5+
<script setup lang="ts">
46
import TestComponent from '@vue-skuilder/courseware/typing/questions/falling-letters/TestComponent.vue'
7+
import { defineAsyncComponent } from 'vue'
8+
9+
const FallingLetters = defineAsyncComponent({
10+
loader: () => import('@vue-skuilder/courseware/typing/questions/falling-letters/FallingLettersWrapper.vue'),
11+
errorComponent: () => 'Failed to load FallingLetters wrapper',
12+
loadingComponent: () => 'Loading FallingLetters wrapper...',
13+
})
514
</script>
615

7-
Test page with simple Vue component from monorepo.
16+
## Test Component (Working)
817

918
<TestComponent />
19+
20+
## FallingLetters Component (Testing 1)
21+
22+
<ClientOnly>
23+
<FallingLetters :data="[{ gameLength: 30, initialSpeed: 1, acceleration: 0.2, spawnInterval: 1 }]" />
24+
</ClientOnly>

packages/courseware/src/typing/questions/falling-letters/FallingLetters.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
<script lang="ts">
6262
import { defineComponent, ref, onMounted, onUnmounted, PropType } from 'vue';
6363
import { useViewable, useQuestionView } from '@vue-skuilder/common-ui';
64-
import { FallingLettersQuestion } from './index';
64+
import { FallingLettersQuestion } from './FallingLettersQuestion';
6565
import { ViewData } from '@vue-skuilder/common';
6666
import { Letter, TreePosition } from './types';
6767
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
// Extracted from index.ts to break circular dependency
2+
import { Question } from '@vue-skuilder/common-ui';
3+
import {
4+
Answer,
5+
DataShape,
6+
DataShapeName,
7+
FieldType,
8+
Status,
9+
ViewData,
10+
} from '@vue-skuilder/common';
11+
12+
const data = function () {
13+
return [
14+
{
15+
gameLength: 30, // 30 seconds game
16+
initialSpeed: 2, // Initial fall speed
17+
acceleration: 0.1, // Speed increase per second
18+
spawnInterval: 1, // New letter every second
19+
},
20+
];
21+
};
22+
23+
export interface Score extends Answer {
24+
win: boolean;
25+
lettersTyped: number;
26+
// the percentage of the gameLength that the user was able to complete
27+
// (0-1)
28+
percentage: number;
29+
}
30+
31+
export class FallingLettersQuestion extends Question {
32+
public static dataShapes: DataShape[] = [
33+
{
34+
name: DataShapeName.TYPING_fallingLetters,
35+
fields: [
36+
{
37+
name: 'gameLength',
38+
type: FieldType.NUMBER,
39+
},
40+
{
41+
name: 'initialSpeed',
42+
type: FieldType.NUMBER,
43+
},
44+
{
45+
name: 'acceleration',
46+
type: FieldType.NUMBER,
47+
},
48+
{
49+
name: 'spawnInterval',
50+
type: FieldType.NUMBER,
51+
validator: {
52+
instructions: 'How often should a new letter spawn? (in seconds)',
53+
test: (input: unknown) => {
54+
if (typeof input === 'string') {
55+
const x = parseFloat(input);
56+
if (x > 0) {
57+
return { status: Status.ok, msg: '' };
58+
} else {
59+
return { status: Status.error, msg: 'Must be greater than 0' };
60+
}
61+
} else {
62+
return { status: Status.error, msg: 'Unexpected non-string input.' };
63+
}
64+
},
65+
placeholder: '1',
66+
},
67+
},
68+
],
69+
},
70+
];
71+
72+
// Note: views will be set in index.ts to avoid circular dependency
73+
public static views: any[] = [];
74+
public static acceptsUserData: boolean = true;
75+
public static seedData = data();
76+
77+
public gameLength: number;
78+
public initialSpeed: number;
79+
public acceleration: number;
80+
public spawnInterval: number;
81+
82+
constructor(data: ViewData[]) {
83+
super(data);
84+
this.gameLength = data[0].gameLength as number;
85+
this.initialSpeed = data[0].initialSpeed as number;
86+
this.acceleration = data[0].acceleration as number;
87+
this.spawnInterval = data[0].spawnInterval as number;
88+
}
89+
90+
evaluate(a: Answer) {
91+
return {
92+
isCorrect: this.isCorrect(a as Score),
93+
performance: this.displayedSkill(a as Score),
94+
};
95+
}
96+
97+
displayedSkill(a: Answer) {
98+
if ((a as Score).win) {
99+
return 1;
100+
} else {
101+
return (a as Score).percentage;
102+
}
103+
}
104+
105+
dataShapes() {
106+
return FallingLettersQuestion.dataShapes;
107+
}
108+
109+
views() {
110+
return FallingLettersQuestion.views;
111+
}
112+
113+
isCorrect(a: Score) {
114+
return a.win;
115+
}
116+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<template>
2+
<div style="border: 2px solid green; padding: 20px; margin: 10px; background: #f0fff0">
3+
<h3>FallingLetters Wrapper</h3>
4+
<p>This is a minimal wrapper to test import chain issues.</p>
5+
<p>Props received: {{ JSON.stringify($props) }}</p>
6+
7+
<!-- Try to load the real component with error boundary -->
8+
<Suspense>
9+
<template #default>
10+
<FallingLettersReal v-bind="$props" />
11+
</template>
12+
<template #fallback>
13+
<div>Loading real FallingLetters component...</div>
14+
</template>
15+
</Suspense>
16+
</div>
17+
</template>
18+
19+
<script setup lang="ts">
20+
import { defineAsyncComponent } from 'vue';
21+
import type { ViewData } from '@vue-skuilder/common';
22+
23+
// Props
24+
defineProps<{
25+
data: ViewData[];
26+
}>();
27+
28+
// Try to load the real component with explicit error handling
29+
const FallingLettersReal = defineAsyncComponent({
30+
loader: async () => {
31+
try {
32+
console.log('Attempting to load FallingLetters.vue directly...');
33+
// Import directly, bypassing any circular dependency through index.ts
34+
const { default: component } = await import('./FallingLetters.vue');
35+
console.log('Successfully loaded FallingLetters.vue:', component);
36+
return component;
37+
} catch (error) {
38+
console.error('Detailed error loading FallingLetters.vue:', error);
39+
throw error;
40+
}
41+
},
42+
onError: (error) => {
43+
console.error('AsyncComponent onError:', error);
44+
},
45+
timeout: 5000,
46+
errorComponent: {
47+
template: '<div style="color: red; padding: 10px;">Error loading FallingLetters: {{ error }}</div>',
48+
props: ['error'],
49+
},
50+
});
51+
</script>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<template>
2+
<div style="border: 2px solid red; padding: 20px; margin: 10px; background: #f9f9f9">
3+
<h2>Test Component from Monorepo</h2>
4+
<p>Simple test component working</p>
5+
<button @click="count++" style="padding: 8px 16px; margin: 5px">Clicked {{ count }} times</button>
6+
</div>
7+
</template>
8+
9+
<script setup lang="ts">
10+
import { ref } from 'vue';
11+
12+
const count = ref(0);
13+
</script>
Lines changed: 5 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -1,116 +1,9 @@
11
// vue/src/courses/typing/questions/falling-letters/index.ts
2-
import { Question } from '@vue-skuilder/common-ui';
3-
import {
4-
Answer,
5-
DataShape,
6-
DataShapeName,
7-
FieldType,
8-
Status,
9-
ViewData,
10-
} from '@vue-skuilder/common';
112
import FallingLettersView from './FallingLetters.vue';
3+
import { FallingLettersQuestion } from './FallingLettersQuestion';
124

13-
const data = function () {
14-
return [
15-
{
16-
gameLength: 30, // 30 seconds game
17-
initialSpeed: 2, // Initial fall speed
18-
acceleration: 0.1, // Speed increase per second
19-
spawnInterval: 1, // New letter every second
20-
},
21-
];
22-
};
5+
// Set the views on the class after import to avoid circular dependency
6+
FallingLettersQuestion.views = [FallingLettersView];
237

24-
export interface Score extends Answer {
25-
win: boolean;
26-
lettersTyped: number;
27-
// the percentage of the gameLength that the user was able to complete
28-
// (0-1)
29-
percentage: number;
30-
}
31-
32-
export class FallingLettersQuestion extends Question {
33-
public static dataShapes: DataShape[] = [
34-
{
35-
name: DataShapeName.TYPING_fallingLetters,
36-
fields: [
37-
{
38-
name: 'gameLength',
39-
type: FieldType.NUMBER,
40-
},
41-
{
42-
name: 'initialSpeed',
43-
type: FieldType.NUMBER,
44-
},
45-
{
46-
name: 'acceleration',
47-
type: FieldType.NUMBER,
48-
},
49-
{
50-
name: 'spawnInterval',
51-
type: FieldType.NUMBER,
52-
validator: {
53-
instructions: 'How often should a new letter spawn? (in seconds)',
54-
test: (input: unknown) => {
55-
if (typeof input === 'string') {
56-
const x = parseFloat(input);
57-
if (x > 0) {
58-
return { status: Status.ok, msg: '' };
59-
} else {
60-
return { status: Status.error, msg: 'Must be greater than 0' };
61-
}
62-
} else {
63-
return { status: Status.error, msg: 'Unexpected non-string input.' };
64-
}
65-
},
66-
placeholder: '1',
67-
},
68-
},
69-
],
70-
},
71-
];
72-
73-
public static views = [FallingLettersView];
74-
public static acceptsUserData: boolean = true;
75-
public static seedData = data();
76-
77-
public gameLength: number;
78-
public initialSpeed: number;
79-
public acceleration: number;
80-
public spawnInterval: number;
81-
82-
constructor(data: ViewData[]) {
83-
super(data);
84-
this.gameLength = data[0].gameLength as number;
85-
this.initialSpeed = data[0].initialSpeed as number;
86-
this.acceleration = data[0].acceleration as number;
87-
this.spawnInterval = data[0].spawnInterval as number;
88-
}
89-
90-
evaluate(a: Answer) {
91-
return {
92-
isCorrect: this.isCorrect(a as Score),
93-
performance: this.displayedSkill(a as Score),
94-
};
95-
}
96-
97-
displayedSkill(a: Answer) {
98-
if ((a as Score).win) {
99-
return 1;
100-
} else {
101-
return (a as Score).percentage;
102-
}
103-
}
104-
105-
dataShapes() {
106-
return FallingLettersQuestion.dataShapes;
107-
}
108-
109-
views() {
110-
return FallingLettersQuestion.views;
111-
}
112-
113-
isCorrect(a: Score) {
114-
return a.win;
115-
}
116-
}
8+
// Re-export everything
9+
export * from './FallingLettersQuestion';

0 commit comments

Comments
 (0)