Skip to content

Commit 7d2a1da

Browse files
committed
🚀 first commit
0 parents  commit 7d2a1da

21 files changed

+12762
-0
lines changed

Diff for: .env.example

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
NUXT_PUBLIC_HELLO_TEXT="Hello from the Edge 👋"

Diff for: .gitignore

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Nuxt dev/build outputs
2+
.output
3+
.data
4+
.nuxt
5+
.nitro
6+
.cache
7+
dist
8+
9+
# Node dependencies
10+
node_modules
11+
12+
# Logs
13+
logs
14+
*.log
15+
16+
# Misc
17+
.DS_Store
18+
.fleet
19+
.idea
20+
21+
# Local env files
22+
.env
23+
.env.*
24+
!.env.example

Diff for: README.md

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Hello Edge
2+
3+
A minimal [Nuxt](https://nuxt.com) starter deployed on the Edge using [NuxtHub](https://hub.nuxt.com).
4+
5+
https://hello.nuxt.dev
6+
7+
<a href="https://hello.nuxt.dev">
8+
<img src="https://github.com/nuxt-hub/hello-edge/assets/904724/99d1bd54-ef7e-4ac9-83ad-0a290f85edcf" alt="Hello World template for NuxtHub" />
9+
</a>
10+
11+
## Features
12+
13+
- Server-Side rendering on Cloudflare Workers
14+
- ESLint setup
15+
- Ready to add a database, blob and KV storage
16+
- One click deploy on 275+ locations for free
17+
18+
## Setup
19+
20+
Make sure to install the dependencies with [pnpm](https://pnpm.io/installation#using-corepack):
21+
22+
```bash
23+
pnpm install
24+
```
25+
26+
You can update the main text displayed by creating a `.env`:
27+
28+
```bash
29+
NUXT_PUBLIC_HELLO_TEXT="Hello my world!"
30+
```
31+
32+
## Development Server
33+
34+
Start the development server on `http://localhost:3000`:
35+
36+
```bash
37+
pnpm dev
38+
```
39+
40+
## Production
41+
42+
Build the application for production:
43+
44+
```bash
45+
pnpm build
46+
```
47+
48+
## Deploy
49+
50+
51+
Deploy the application on the Edge with [NuxtHub](https://hub.nuxt.com) on your Cloudflare account:
52+
53+
```bash
54+
npx nuxthub deploy
55+
```
56+
57+
Then checkout your server logs, analaytics and more in the [NuxtHub Admin](https://admin.hub.nuxt.com).
58+
59+
You can also deploy using [Cloudflare Pages CI](https://hub.nuxt.com/docs/getting-started/deploy#cloudflare-pages-ci).
60+

Diff for: app/app.config.ts

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export default defineAppConfig({
2+
ui: {
3+
primary: 'red',
4+
},
5+
})

Diff for: app/app.vue

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<template>
2+
<NuxtRouteAnnouncer />
3+
<NuxtLoadingIndicator color="rgb(var(--color-primary-DEFAULT))" />
4+
<div class="flex flex-col items-center justify-center h-screen max-w-[600px] w-full lg:w-[600px] mx-auto">
5+
<NuxtPage />
6+
</div>
7+
</template>

Diff for: app/components/ColorModeToggle.vue

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<script setup lang="ts">
2+
const colorMode = useColorMode()
3+
4+
const isDark = computed({
5+
get() {
6+
return colorMode.value === 'dark'
7+
},
8+
set() {
9+
colorMode.preference = colorMode.value === 'dark' ? 'light' : 'dark'
10+
},
11+
})
12+
</script>
13+
14+
<template>
15+
<ClientOnly v-if="!colorMode?.forced">
16+
<UToggle
17+
v-bind="$attrs"
18+
v-model="isDark"
19+
on-icon="i-heroicons-moon"
20+
off-icon="i-heroicons-sun"
21+
:aria-label="`Switch to ${isDark ? 'light' : 'dark'} mode`"
22+
/>
23+
24+
<template #fallback>
25+
<UToggle
26+
v-bind="$attrs"
27+
on-icon="i-heroicons-moon"
28+
off-icon="i-heroicons-sun"
29+
:aria-label="`Switch to ${isDark ? 'light' : 'dark'} mode`"
30+
disabled
31+
/>
32+
</template>
33+
</ClientOnly>
34+
</template>

Diff for: app/components/ColorPicker/ColorPicker.vue

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<!-- Source: https://github.com/nuxt/ui/blob/dev/docs/components/color-picker/ColorPicker.vue -->
2+
<script setup lang="ts">
3+
import colors from '#tailwind-config/theme/colors'
4+
5+
const appConfig = useAppConfig()
6+
const colorMode = useColorMode()
7+
8+
// Computed
9+
10+
const primaryColors = computed(() =>
11+
appConfig.ui.colors
12+
.filter(color => color !== 'primary')
13+
.map(color => ({ value: color, text: color, hex: colors[color][colorMode.value === 'dark' ? 400 : 500] })),
14+
)
15+
const primary = computed({
16+
get() {
17+
return primaryColors.value.find(option => option.value === appConfig.ui.primary)
18+
},
19+
set(option) {
20+
appConfig.ui.primary = option.value
21+
window.localStorage.setItem('nuxt-ui-primary', appConfig.ui.primary)
22+
},
23+
})
24+
25+
const grayColors = computed(() =>
26+
['slate', 'cool', 'zinc', 'neutral', 'stone'].map(color => ({
27+
value: color,
28+
text: color,
29+
hex: colors[color][colorMode.value === 'dark' ? 400 : 500],
30+
})),
31+
)
32+
const gray = computed({
33+
get() {
34+
return grayColors.value.find(option => option.value === appConfig.ui.gray)
35+
},
36+
set(option) {
37+
appConfig.ui.gray = option.value
38+
window.localStorage.setItem('nuxt-ui-gray', appConfig.ui.gray)
39+
},
40+
})
41+
</script>
42+
43+
<template>
44+
<UPopover mode="hover">
45+
<template #default="{ open }">
46+
<UButton
47+
color="gray"
48+
variant="ghost"
49+
square
50+
:class="[open && 'bg-gray-50 dark:bg-gray-800']"
51+
aria-label="Color picker"
52+
size="xs"
53+
>
54+
<UIcon name="i-heroicons-paint-brush-20-solid" class="w-4 h-4 text-primary-500 dark:text-primary-400" />
55+
</UButton>
56+
</template>
57+
58+
<template #panel>
59+
<div class="p-2">
60+
<div class="grid grid-cols-5 gap-px">
61+
<ColorPickerPill
62+
v-for="color in primaryColors"
63+
:key="color.value"
64+
:color="color"
65+
:selected="primary"
66+
@select="primary = color"
67+
/>
68+
</div>
69+
70+
<hr class="border-gray-200 dark:border-gray-800 my-2">
71+
72+
<div class="grid grid-cols-5 gap-px">
73+
<ColorPickerPill
74+
v-for="color in grayColors"
75+
:key="color.value"
76+
:color="color"
77+
:selected="gray"
78+
@select="gray = color"
79+
/>
80+
</div>
81+
82+
<hr class="border-gray-200 dark:border-gray-800 my-2">
83+
84+
<ColorModeToggle />
85+
</div>
86+
</template>
87+
</UPopover>
88+
</template>

Diff for: app/components/ColorPicker/ColorPickerPill.vue

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<!-- Source: https://github.com/nuxt/ui/blob/dev/docs/components/color-picker/ColorPickerPill.vue -->
2+
<script setup lang="ts">
3+
defineProps<{ color: { value: string; hex: string }; selected: { value: string } }>()
4+
defineEmits(['select'])
5+
</script>
6+
7+
<template>
8+
<UTooltip :text="color.value" class="capitalize" :open-delay="500">
9+
<UButton
10+
color="white"
11+
square
12+
:ui="{
13+
color: {
14+
white: {
15+
solid: 'ring-0 bg-gray-100 dark:bg-gray-800 hover:bg-gray-100 dark:hover:bg-gray-800',
16+
ghost: 'hover:bg-gray-50 dark:hover:bg-gray-800/50',
17+
},
18+
},
19+
}"
20+
:variant="color.value === selected.value ? 'solid' : 'ghost'"
21+
@click.stop.prevent="$emit('select')"
22+
>
23+
<span class="inline-block w-3 h-3 rounded-full" :style="{ backgroundColor: color.hex }" />
24+
</UButton>
25+
</UTooltip>
26+
</template>

Diff for: app/components/NPMChart.vue

+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
<script setup lang="ts">
2+
import { eachDayOfInterval, eachWeekOfInterval, eachMonthOfInterval, format } from 'date-fns'
3+
import { VisXYContainer, VisLine, VisAxis, VisArea, VisCrosshair, VisTooltip } from '@unovis/vue'
4+
5+
const cardRef = ref<HTMLElement | null>(null)
6+
const { width } = useElementSize(cardRef)
7+
8+
const props = defineProps({
9+
data: {
10+
type: Object as PropType<Record<string, number>>,
11+
required: true
12+
},
13+
period: {
14+
type: String as PropType<Period>,
15+
default: 'monthly'
16+
}
17+
})
18+
19+
type Period = 'weekly' | 'monthly'
20+
21+
interface DataRecord {
22+
date: Date
23+
downloads: number
24+
}
25+
26+
const x = (_: DataRecord, i: number) => i
27+
const y = (d: DataRecord) => d.amount
28+
29+
const data = computed(() => {
30+
const periodData = {}
31+
const periodFormat = props.period === 'monthly' ? 'MM-yyyy' : 'ww-yyyy'
32+
for (const date in props.data) {
33+
const period = format(date, periodFormat)
34+
periodData[period] ||= { amount: 0, date }
35+
periodData[period].amount += props.data[date]
36+
}
37+
return Object.entries(periodData).map(([period, { date, amount }]) => ({ date: formatDate(date), amount }))
38+
})
39+
40+
const total = computed(() => data.value.reduce((acc: number, { amount }) => acc + amount, 0))
41+
42+
const formatNumber = new Intl.NumberFormat('en', { maximumFractionDigits: 0 }).format
43+
const formatNumberCompact = new Intl.NumberFormat('en', { maximumFractionDigits: 0, notation: 'compact' }).format
44+
45+
const formatDate = (date: Date): string => {
46+
return ({
47+
weekly: format(date, 'd MMM'),
48+
monthly: format(date, 'MMM yyy')
49+
})[props.period]
50+
}
51+
52+
const xTicks = (i: number) => {
53+
if (i === 0 || i === data.value.length - 1 || !data.value[i]) {
54+
return ''
55+
}
56+
57+
return formatDate(data.value[i].date)
58+
}
59+
const template = (d: DataRecord) => `${formatDate(d.date)}: ${formatNumber(d.amount)}`
60+
</script>
61+
62+
<template>
63+
<div class="flex flex-col gap-2 w-full lg:w-[600px]" ref="cardRef">
64+
<div class="text-sm text-gray-600 dark:text-gray-400">{{ formatNumber(total) }} total npm downloads.</div>
65+
<VisXYContainer :data="data" class="h-96" :width="width">
66+
<VisLine :x="x" :y="y" color="rgb(var(--color-primary-DEFAULT))" />
67+
<VisArea :x="x" :y="y" color="rgb(var(--color-primary-DEFAULT))" :opacity="0.1" />
68+
69+
<VisAxis type="x" :x="x" :tick-format="xTicks" />
70+
<VisAxis type="y" :tick-format="(y) => formatNumberCompact(y)" />
71+
72+
<VisCrosshair color="rgb(var(--color-primary-DEFAULT))" :template="template" />
73+
74+
<VisTooltip />
75+
</VisXYContainer>
76+
</div>
77+
</template>
78+
79+
<style scoped>
80+
.unovis-xy-container {
81+
--vis-crosshair-line-stroke-color: rgb(var(--color-primary-500));
82+
--vis-crosshair-circle-stroke-color: #fff;
83+
84+
--vis-axis-grid-color: rgb(var(--color-gray-200));
85+
--vis-axis-tick-color: rgb(var(--color-gray-200));
86+
--vis-axis-tick-label-color: rgb(var(--color-gray-400));
87+
88+
--vis-tooltip-background-color: #fff;
89+
--vis-tooltip-border-color: rgb(var(--color-gray-200));
90+
--vis-tooltip-text-color: rgb(var(--color-gray-900));
91+
}
92+
93+
.dark {
94+
.unovis-xy-container {
95+
--vis-crosshair-line-stroke-color: rgb(var(--color-primary-400));
96+
--vis-crosshair-circle-stroke-color: rgb(var(--color-gray-900));
97+
98+
--vis-axis-grid-color: rgb(var(--color-gray-800));
99+
--vis-axis-tick-color: rgb(var(--color-gray-800));
100+
--vis-axis-tick-label-color: rgb(var(--color-gray-500));
101+
102+
--vis-tooltip-background-color: rgb(var(--color-gray-900));
103+
--vis-tooltip-border-color: rgb(var(--color-gray-800));
104+
--vis-tooltip-text-color: #fff;
105+
}
106+
}
107+
</style>

0 commit comments

Comments
 (0)