🎉 Commits project
This commit is contained in:
123
src/App.vue
Normal file
123
src/App.vue
Normal file
@@ -0,0 +1,123 @@
|
||||
<template>
|
||||
<div class="flex items-center justify-center w-screen h-screen bg-gray-900">
|
||||
<div class="flex items-center justify-center flex-col gap-y-2 w-72">
|
||||
<div class="flex gap-x-2">
|
||||
<button v-if="level > 0" @click="changeLevel(-1)">
|
||||
<img src="/chevron.svg" alt="Arrow" class="rotate-90" />
|
||||
</button>
|
||||
|
||||
<p class="text-emerald-100 text-xl font-bold">
|
||||
{{ levels[level].name }}
|
||||
</p>
|
||||
|
||||
<button v-if="level < levels.length - 1" @click="changeLevel(+1)">
|
||||
<img src="/chevron.svg" alt="Arrow" class="-rotate-90" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="problem" class="flex">
|
||||
<div class="flex items-end justify-center flex-col h-full gap-y-2 p-2">
|
||||
<Number
|
||||
:number="problem.a"
|
||||
:options="{ spacing: +spacing, fill: fill }"
|
||||
class="mx-2"
|
||||
/>
|
||||
<Number
|
||||
:number="problem.b"
|
||||
:options="{
|
||||
spacing: +spacing,
|
||||
fill: fill,
|
||||
prefix: problem.operator,
|
||||
}"
|
||||
class="mx-2"
|
||||
/>
|
||||
|
||||
<div class="w-full h-px bg-emerald-100" />
|
||||
|
||||
<Number
|
||||
:data-shown="shown"
|
||||
:number="problem.answer"
|
||||
:options="{ spacing: +spacing, fill: fill }"
|
||||
class="mx-2 data-[shown=false]:opacity-0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex w-full items-center justify-between mt-4">
|
||||
<button @click="() => (openSettings = !openSettings)" class="ml-4">
|
||||
<img
|
||||
:data-open="openSettings"
|
||||
src="/chevron.svg"
|
||||
alt="Chevron"
|
||||
class="data-[open=true]:rotate-180"
|
||||
/>
|
||||
</button>
|
||||
|
||||
<button
|
||||
v-if="shown"
|
||||
@click="next"
|
||||
class="text-emerald-100 border border-emerald-100 rounded-full px-4 py-1"
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
<button
|
||||
v-else
|
||||
@click="show"
|
||||
class="text-emerald-100 border border-emerald-100 rounded-full px-4 py-1"
|
||||
>
|
||||
Show
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="openSettings" class="flex gap-x-2 mt-4">
|
||||
<label for="spacing" class="text-emerald-100">Spacing:</label>
|
||||
|
||||
<select
|
||||
v-model="spacing"
|
||||
name="spacing"
|
||||
id="spacing"
|
||||
class="text-emerald-100 bg-transparent"
|
||||
>
|
||||
<option value="2">2</option>
|
||||
<option value="3">3</option>
|
||||
<option value="4" selected>4</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div v-if="openSettings" class="flex gap-x-2">
|
||||
<input v-model="fill" type="checkbox" id="fill" name="fill" checked />
|
||||
<label for="fill" class="text-emerald-100">Fill</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from "vue";
|
||||
import { levels, Problem } from "./composables/levels.ts";
|
||||
import Number from "./components/Number.vue";
|
||||
|
||||
const level = ref(0);
|
||||
const problem = ref<Problem | undefined>();
|
||||
const shown = ref(false);
|
||||
|
||||
const openSettings = ref(false);
|
||||
const spacing = ref("4");
|
||||
const fill = ref(true);
|
||||
|
||||
function next() {
|
||||
problem.value = levels[level.value].generate();
|
||||
shown.value = false;
|
||||
}
|
||||
|
||||
function show() {
|
||||
shown.value = true;
|
||||
}
|
||||
|
||||
function changeLevel(by: number) {
|
||||
level.value += by;
|
||||
next();
|
||||
}
|
||||
|
||||
onMounted(next);
|
||||
</script>
|
1
src/assets/vue.svg
Normal file
1
src/assets/vue.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>
|
After Width: | Height: | Size: 496 B |
11
src/components/Bit.vue
Normal file
11
src/components/Bit.vue
Normal file
@@ -0,0 +1,11 @@
|
||||
<template>
|
||||
<div v-if="bit === '1'" class="w-2 h-8 bg-emerald-100 rounded-t-full" />
|
||||
<div v-else-if="bit === '0'" class="w-2 h-3 bg-emerald-100 rounded-t-full" />
|
||||
<p v-else class="text-emerald-100 text-3xl">{{ bit }}</p>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
bit: string;
|
||||
}>();
|
||||
</script>
|
15
src/components/Group.vue
Normal file
15
src/components/Group.vue
Normal file
@@ -0,0 +1,15 @@
|
||||
<template>
|
||||
<div
|
||||
class="flex items-end gap-x-1 border-b-[0.375rem] border-b-emerald-100 border-solid w-fit rounded-md"
|
||||
>
|
||||
<Bit v-for="bit in group" :bit="bit" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Bit from "./Bit.vue";
|
||||
|
||||
defineProps<{
|
||||
group: string[];
|
||||
}>();
|
||||
</script>
|
59
src/components/Number.vue
Normal file
59
src/components/Number.vue
Normal file
@@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<div class="flex gap-x-2 items-end justify-end w-fit h-[2.375rem]">
|
||||
<p v-if="coefficient === -1" class="text-emerald-100 text-3xl">-</p>
|
||||
<p v-if="options.prefix" class="text-emerald-100 text-3xl">
|
||||
{{ options.prefix }}
|
||||
</p>
|
||||
|
||||
<Group v-for="group in binary" :group="group" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from "vue";
|
||||
import Group from "./Group.vue";
|
||||
|
||||
interface Options {
|
||||
spacing: number | undefined;
|
||||
fill: boolean | undefined;
|
||||
prefix: string | undefined;
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
number: number;
|
||||
options: Options;
|
||||
}>();
|
||||
|
||||
const coefficient = computed(() => {
|
||||
return Math.abs(props.number) / props.number; // 1 or -1 based on the number
|
||||
});
|
||||
const binary = computed(() => {
|
||||
const string = Number(Math.abs(props.number)).toString(2);
|
||||
const spacing = props.options.spacing || 4;
|
||||
const fill = props.options.fill ?? true;
|
||||
|
||||
const result: string[][] = [];
|
||||
let chunk: string[] = [];
|
||||
|
||||
for (let i = 0; i < string.length; i++) {
|
||||
if (i % spacing === 0 && i !== 0) {
|
||||
result.push(chunk.reverse());
|
||||
chunk = [];
|
||||
}
|
||||
|
||||
chunk.push(string[string.length - i - 1]);
|
||||
}
|
||||
|
||||
if (chunk.length !== 0) {
|
||||
if (fill) {
|
||||
while (chunk.length !== spacing) {
|
||||
chunk.push("0");
|
||||
}
|
||||
}
|
||||
|
||||
result.push(chunk.reverse());
|
||||
}
|
||||
|
||||
return result.reverse();
|
||||
});
|
||||
</script>
|
80
src/composables/levels.ts
Normal file
80
src/composables/levels.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
export interface Level {
|
||||
name: string;
|
||||
generate: () => Problem;
|
||||
}
|
||||
|
||||
export interface Problem {
|
||||
a: number;
|
||||
operator: string;
|
||||
b: number;
|
||||
answer: number;
|
||||
}
|
||||
|
||||
export function random(max: number) {
|
||||
return Math.floor(Math.random() * max);
|
||||
}
|
||||
|
||||
function AdditionLevel(level: 1 | 2 | 3): Level {
|
||||
return {
|
||||
name: `Addition Level ${level}`,
|
||||
generate: () => {
|
||||
const a = random(2 ** (4 * level));
|
||||
const b = random(2 ** (4 * level));
|
||||
const answer = a + b;
|
||||
|
||||
return { a, operator: "+", b, answer };
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function SubtractionLevel(level: 1 | 2 | 3): Level {
|
||||
return {
|
||||
name: `Subtraction Level ${level}`,
|
||||
generate: () => {
|
||||
const a = random(2 ** (4 * level));
|
||||
const b = random(2 ** (4 * level));
|
||||
const answer = a - b;
|
||||
|
||||
return { a, operator: "-", b, answer };
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function MultiplicationLevel(level: 1 | 2): Level {
|
||||
return {
|
||||
name: `Multiplication Level ${level}`,
|
||||
generate: () => {
|
||||
const a = random(2 ** (4 * level));
|
||||
const b = random(2 ** (4 * level));
|
||||
const answer = a * b;
|
||||
|
||||
return { a, operator: "*", b, answer };
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function DivisionLevel(level: 1 | 2): Level {
|
||||
return {
|
||||
name: `Division Level ${level}`,
|
||||
generate: () => {
|
||||
const a = random(2 ** (4 * level));
|
||||
const b = random(2 ** (4 * level));
|
||||
const answer = a / b;
|
||||
|
||||
return { a, operator: "/", b, answer };
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export const levels: Level[] = [
|
||||
AdditionLevel(1),
|
||||
SubtractionLevel(1),
|
||||
AdditionLevel(2),
|
||||
SubtractionLevel(2),
|
||||
AdditionLevel(3),
|
||||
SubtractionLevel(3),
|
||||
MultiplicationLevel(1),
|
||||
DivisionLevel(1),
|
||||
MultiplicationLevel(2),
|
||||
DivisionLevel(2),
|
||||
];
|
3
src/index.css
Normal file
3
src/index.css
Normal file
@@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
5
src/main.ts
Normal file
5
src/main.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { createApp } from "vue";
|
||||
import App from "./App.vue";
|
||||
import "./index.css";
|
||||
|
||||
createApp(App).mount("#app");
|
1
src/vite-env.d.ts
vendored
Normal file
1
src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
Reference in New Issue
Block a user