🎉 Commits project
This commit is contained in:
commit
75aed63005
28
.gitignore
vendored
Normal file
28
.gitignore
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
|
|
||||||
|
bun.lockb
|
||||||
|
package-lock.json
|
||||||
|
.vscode/
|
18
README.md
Normal file
18
README.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Vue 3 + TypeScript + Vite
|
||||||
|
|
||||||
|
This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
|
||||||
|
|
||||||
|
## Recommended IDE Setup
|
||||||
|
|
||||||
|
- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
|
||||||
|
|
||||||
|
## Type Support For `.vue` Imports in TS
|
||||||
|
|
||||||
|
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types.
|
||||||
|
|
||||||
|
If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps:
|
||||||
|
|
||||||
|
1. Disable the built-in TypeScript Extension
|
||||||
|
1. Run `Extensions: Show Built-in Extensions` from VSCode's command palette
|
||||||
|
2. Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)`
|
||||||
|
2. Reload the VSCode window by running `Developer: Reload Window` from the command palette.
|
20
index.html
Normal file
20
index.html
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8"/>
|
||||||
|
<link rel="icon" type="image/png" href="/binary.png"/>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||||
|
<title>Binary</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.ts"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
html, body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
</style>
|
24
package.json
Normal file
24
package.json
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"name": "binary",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vue-tsc && vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"vue": "^3.4.19"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vitejs/plugin-vue": "^5.0.4",
|
||||||
|
"autoprefixer": "^10.4.18",
|
||||||
|
"postcss": "^8.4.35",
|
||||||
|
"prettier": "^3.2.5",
|
||||||
|
"tailwindcss": "^3.4.1",
|
||||||
|
"typescript": "^5.2.2",
|
||||||
|
"vite": "^5.1.4",
|
||||||
|
"vue-tsc": "^1.8.27"
|
||||||
|
}
|
||||||
|
}
|
6
postcss.config.js
Normal file
6
postcss.config.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export default {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
}
|
BIN
public/binary.png
Normal file
BIN
public/binary.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.4 KiB |
3
public/chevron.svg
Normal file
3
public/chevron.svg
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24" fill="#d1fae5">
|
||||||
|
<path d="M480-345 240-585l56-56 184 184 184-184 56 56-240 240Z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 180 B |
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" />
|
8
tailwind.config.js
Normal file
8
tailwind.config.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
export default {
|
||||||
|
content: ["./index.html", "./src/**/*.{vue,js,ts,jsx,tsx}"],
|
||||||
|
theme: {
|
||||||
|
extend: {},
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
};
|
25
tsconfig.json
Normal file
25
tsconfig.json
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||||
|
"skipLibCheck": true,
|
||||||
|
|
||||||
|
/* Bundler mode */
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
|
||||||
|
/* Linting */
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true
|
||||||
|
},
|
||||||
|
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"],
|
||||||
|
"references": [{ "path": "./tsconfig.node.json" }]
|
||||||
|
}
|
11
tsconfig.node.json
Normal file
11
tsconfig.node.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"strict": true
|
||||||
|
},
|
||||||
|
"include": ["vite.config.ts"]
|
||||||
|
}
|
7
vite.config.ts
Normal file
7
vite.config.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { defineConfig } from "vite";
|
||||||
|
import vue from "@vitejs/plugin-vue";
|
||||||
|
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [vue()],
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user