🎉 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