🎉 Commits project

This commit is contained in:
Daniel Svitan
2024-03-04 18:35:01 +01:00
commit 75aed63005
20 changed files with 448 additions and 0 deletions

123
src/App.vue Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View File

@@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

5
src/main.ts Normal file
View 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
View File

@@ -0,0 +1 @@
/// <reference types="vite/client" />