Compare commits
12 Commits
85344ef306
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a3dc1a47c | ||
|
|
570f605ac7 | ||
|
|
da0231b427 | ||
|
|
9c49abfff2 | ||
|
|
3b9b26f993 | ||
|
|
1d43b87836 | ||
|
|
d184a8339c | ||
|
|
bf9f27068d | ||
|
|
e4fb05aa27 | ||
|
|
251ffa2e1a | ||
|
|
2986a5d0fe | ||
|
|
025cf4072e |
1
.node-version
Normal file
1
.node-version
Normal file
@@ -0,0 +1 @@
|
|||||||
|
v23
|
||||||
44
README.md
44
README.md
@@ -1,18 +1,40 @@
|
|||||||
# Vue 3 + TypeScript + Vite
|
# miniminu - the milestone reminder
|
||||||
|
|
||||||
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.
|
A minimal countdown timer app. Set a target date and watch the time remaining displayed in years, months, days, hours, minutes, and seconds.
|
||||||
|
|
||||||
## Recommended IDE Setup
|
## Features
|
||||||
|
|
||||||
- [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).
|
- Countdown to any target date
|
||||||
|
- Optional project title (shown as page title)
|
||||||
|
- Shareable URL — state is stored in query params (`?project=...&target=...`)
|
||||||
|
- Responsive: form controls are hidden on small screens, visible on 600px+
|
||||||
|
|
||||||
## Type Support For `.vue` Imports in TS
|
## Usage
|
||||||
|
|
||||||
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.
|
Open the app in a browser. On a wide enough screen, fill in the **Title** and **Target date** fields, then click **copy url** to share a link with the countdown pre-configured.
|
||||||
|
|
||||||
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:
|
URL params:
|
||||||
|
- `project` — display name / page title
|
||||||
|
- `target` — target date in `YYYY-MM-DD` format
|
||||||
|
|
||||||
1. Disable the built-in TypeScript Extension
|
## Development
|
||||||
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)`
|
```bash
|
||||||
2. Reload the VSCode window by running `Developer: Reload Window` from the command palette.
|
pnpm install
|
||||||
|
pnpm dev
|
||||||
|
```
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm build
|
||||||
|
pnpm preview
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tech stack
|
||||||
|
|
||||||
|
- [Vue 3](https://vuejs.org/) with `<script setup>` SFCs
|
||||||
|
- [TypeScript](https://www.typescriptlang.org/)
|
||||||
|
- [Vite 8](https://vite.dev/)
|
||||||
|
- [Luxon](https://moment.github.io/luxon/) for date/time calculations
|
||||||
|
- [VueUse](https://vueuse.org/) for URL search params and title sync
|
||||||
|
|||||||
8
nixpacks.toml
Normal file
8
nixpacks.toml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
[phases.setup]
|
||||||
|
nixPkgs = ["nodejs_23", "pnpm"]
|
||||||
|
|
||||||
|
[phases.install]
|
||||||
|
cmds = ["pnpm install --no-frozen-lockfile --force"]
|
||||||
|
|
||||||
|
[phases.build]
|
||||||
|
cmds = ["pnpm build"]
|
||||||
27
package.json
27
package.json
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "miniminu",
|
"name": "miniminu",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.0.0",
|
"version": "1.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
@@ -10,17 +10,24 @@
|
|||||||
"test": "vitest"
|
"test": "vitest"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vueuse/core": "^9.13.0",
|
"@vueuse/core": "^14.2.1",
|
||||||
"luxon": "^3.3.0",
|
"luxon": "^3.7.2",
|
||||||
"vue": "^3.2.45",
|
"vue": "^3.5.30",
|
||||||
"vue-router": "4"
|
"vue-router": "4"
|
||||||
},
|
},
|
||||||
|
"pnpm": {
|
||||||
|
"supportedArchitectures": {
|
||||||
|
"os": ["current", "linux"],
|
||||||
|
"cpu": ["current", "x64"],
|
||||||
|
"libc": ["current", "glibc"]
|
||||||
|
}
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/luxon": "^3.2.0",
|
"@types/luxon": "^3.7.1",
|
||||||
"@vitejs/plugin-vue": "^4.0.0",
|
"@vitejs/plugin-vue": "^6.0.5",
|
||||||
"typescript": "^4.9.3",
|
"typescript": "^5.9.3",
|
||||||
"vite": "^4.1.0",
|
"vite": "^8.0.0",
|
||||||
"vitest": "^0.29.2",
|
"vitest": "^4.1.0",
|
||||||
"vue-tsc": "^1.0.24"
|
"vue-tsc": "^3.2.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
1783
pnpm-lock.yaml
generated
1783
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,7 @@ import { useTimeUntil } from "../hooks/useTimeUntil.hooks"
|
|||||||
|
|
||||||
const props = defineProps<{ project?: string; target?: string }>()
|
const props = defineProps<{ project?: string; target?: string }>()
|
||||||
const searchParams = useUrlSearchParams<{ project?: string; target?: string }>(
|
const searchParams = useUrlSearchParams<{ project?: string; target?: string }>(
|
||||||
"history"
|
"history",
|
||||||
)
|
)
|
||||||
|
|
||||||
const projectTitle = ref(props.project)
|
const projectTitle = ref(props.project)
|
||||||
@@ -18,7 +18,7 @@ watch(
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
immediate: true,
|
immediate: true,
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
@@ -28,7 +28,7 @@ watch(
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
immediate: true,
|
immediate: true,
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
const target = computed(() => targetInput.value)
|
const target = computed(() => targetInput.value)
|
||||||
@@ -42,7 +42,7 @@ const targetDate = computed(() =>
|
|||||||
? new Date(targetInput.value).toLocaleDateString(undefined, {
|
? new Date(targetInput.value).toLocaleDateString(undefined, {
|
||||||
dateStyle: "long",
|
dateStyle: "long",
|
||||||
})
|
})
|
||||||
: null
|
: null,
|
||||||
)
|
)
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -129,7 +129,7 @@ const copyUrl = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="target">Target date:</label>
|
<label for="target">Milestone:</label>
|
||||||
<input
|
<input
|
||||||
type="date"
|
type="date"
|
||||||
id="target"
|
id="target"
|
||||||
@@ -138,8 +138,12 @@ const copyUrl = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
<a :href="url" target="_blank" rel="noopener noreferrer"
|
||||||
|
><button type="button">open url</button></a
|
||||||
|
>
|
||||||
<button @click="copyUrl">copy url</button>
|
<button @click="copyUrl">copy url</button>
|
||||||
</div>
|
</div>
|
||||||
|
<p class="hint">Shrink the window to hide this config.</p>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -207,4 +211,12 @@ input {
|
|||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hint {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
opacity: 0.5;
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user