Skip to content

Commit 931dd7b

Browse files
committed
📘 doc: dark theme
1 parent 1940de9 commit 931dd7b

File tree

13 files changed

+1388
-338
lines changed

13 files changed

+1388
-338
lines changed

components/midori/hero.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ watch(copied, (value) => {
9090
}
9191
})
9292
93-
import { useData } from 'vitepress'
93+
import useDark from './use-dark'
9494
95-
const { isDark } = useData()
95+
const isDark = useDark()
9696
</script>

components/midori/index.vue

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,11 @@ import 'prismjs/components/prism-typescript'
1818
import '../tailwind.css'
1919
import './midori.css'
2020
21-
import { onMounted, ref } from 'vue'
22-
import { useData } from 'vitepress'
23-
2421
import BuiltWithLove from './built-with-love.vue'
2522
26-
const { isDark } = useData()
23+
import useDark from './use-dark'
24+
25+
const isDark = useDark()
2726
</script>
2827

2928
<template>

components/midori/plugins.vue

Lines changed: 13 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -24,22 +24,17 @@ const plugins = [
2424
]
2525
] as const
2626
27-
import { onMounted, ref } from 'vue'
28-
import { useData } from 'vitepress'
29-
30-
const { isDark } = useData()
27+
import useDark from './use-dark'
3128
29+
const isDark = useDark()
3230
</script>
3331

3432
<template>
35-
<article
36-
class="flex justify-between flex-col lg:flex-row items-center w-full max-w-6xl mx-auto my-8 gap-12"
37-
>
33+
<article class="flex justify-between flex-col lg:flex-row items-center w-full max-w-6xl mx-auto my-8 gap-12">
3834
<section class="flex flex-col w-full max-w-lg">
3935
<header class="flex flex-col justify-center items-start">
4036
<h2
41-
class="text-5xl md:text-6xl leading-tight font-bold bg-clip-text text-transparent bg-gradient-to-tr from-sky-400 to-fuchsia-400 mb-4"
42-
>
37+
class="text-5xl md:text-6xl leading-tight font-bold bg-clip-text text-transparent bg-gradient-to-tr from-sky-400 to-fuchsia-400 mb-4">
4338
It works with that
4439
</h2>
4540
</header>
@@ -52,32 +47,20 @@ const { isDark } = useData()
5247
it with the community.
5348
</p>
5449
<div class="mt-8">
55-
<a
56-
href="/plugins/overview"
57-
class="text-xl font-medium bg-blue-200/25 dark:bg-blue-500/25 text-blue-500 px-6 py-3 rounded-xl"
58-
>
50+
<a href="/plugins/overview"
51+
class="text-xl font-medium bg-blue-200/25 dark:bg-blue-500/25 text-blue-500 px-6 py-3 rounded-xl">
5952
Explore plugins
6053
</a>
6154
</div>
6255
</section>
6356
<section class="flex flex-col w-full max-w-xl">
64-
<ol
65-
class="grid grid-cols-2 sm:grid-cols-3 gap-4 list-none w-full text-gray-500 dark:text-gray-400 text-lg"
66-
>
67-
<li
68-
v-for="[name, icon, href, darkIcon = ''] in plugins"
69-
class="aspect-square rounded-xl border dark:border-gray-700 bg-gradient-to-br from-white to-gray-50 dark:from-gray-700/75 dark:to-gray-800/75 text-xl transform transition-all ease-out duration-200 hover:shadow-lg focus:shadow-lg hover:-translate-y-1.5 focus:-translate-y-1.5"
70-
>
71-
<a
72-
:href="href"
73-
:target="href.startsWith('http') ? '_blank' : ''"
74-
class="flex flex-col justify-center items-center gap-3 w-full h-full text-lg font-medium text-gray-600 dark:text-gray-400 overflow-hidden"
75-
>
76-
<img
77-
:src="isDark && darkIcon ? darkIcon ?? icon : icon"
78-
:alt="name"
79-
class="absolute w-36 h-36 opacity-10 filter blur-lg"
80-
/>
57+
<ol class="grid grid-cols-2 sm:grid-cols-3 gap-4 list-none w-full text-gray-500 dark:text-gray-400 text-lg">
58+
<li v-for="[name, icon, href, darkIcon = ''] in plugins"
59+
class="aspect-square rounded-xl border dark:border-gray-700 bg-gradient-to-br from-white to-gray-50 dark:from-gray-700/75 dark:to-gray-800/75 text-xl transform transition-all ease-out duration-200 hover:shadow-lg focus:shadow-lg hover:-translate-y-1.5 focus:-translate-y-1.5">
60+
<a :href="href" :target="href.startsWith('http') ? '_blank' : ''"
61+
class="flex flex-col justify-center items-center gap-3 w-full h-full text-lg font-medium text-gray-600 dark:text-gray-400 overflow-hidden">
62+
<img :src="isDark && darkIcon ? darkIcon ?? icon : icon" :alt="name"
63+
class="absolute w-36 h-36 opacity-10 filter blur-lg" />
8164
<img :src="isDark && darkIcon ? darkIcon ?? icon : icon" :alt="name" class="h-14" />
8265
{{ name }}
8366
</a>

components/midori/ray.vue

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<!-- https://codepen.io/TWilson/pen/jOdWqbZ -->
22
<template>
3-
<div class="absolute flex flex-col w-full items-center justify-center bg-transparent transition-bg" :class="className">
3+
<div class="absolute flex flex-col w-full !max-w-full items-center justify-center bg-transparent transition-bg overflow-hidden"
4+
:class="className">
45
<div class="jumbo absolute -inset-[10px] opacity-50" :class="{ '-safari': isSafari }" />
56
</div>
67
</template>
@@ -84,7 +85,14 @@ const props = defineProps<{
8485
8586
const className = ref(props.class || 'h-screen')
8687
const isSafari = ref(
87-
navigator.userAgent.indexOf('Safari') !== -1 &&
88-
navigator.userAgent.indexOf('Chrome') === -1
88+
typeof window !== "undefined" ?
89+
navigator.userAgent.indexOf('Safari') !== -1 &&
90+
navigator.userAgent.indexOf('Chrome') === -1 : false
8991
)
92+
93+
onMounted(() => {
94+
isSafari.value =
95+
navigator.userAgent.indexOf('Safari') !== -1 &&
96+
navigator.userAgent.indexOf('Chrome') === -1
97+
})
9098
</script>

components/midori/use-dark.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { onMounted, ref } from 'vue'
2+
3+
export default function useDark() {
4+
const isDark = ref(false)
5+
6+
onMounted(() => {
7+
// @ts-ignore
8+
isDark.value = document.documentElement.classList.contains('dark')
9+
10+
// @ts-ignore
11+
const attrObserver = new MutationObserver((mutations) => {
12+
// @ts-ignore
13+
mutations.forEach((mutation) => {
14+
if (mutation.attributeName !== 'class') return
15+
16+
isDark.value =
17+
// @ts-ignore
18+
document.documentElement.classList.contains('dark')
19+
})
20+
})
21+
22+
// @ts-ignore
23+
attrObserver.observe(document.documentElement, { attributes: true })
24+
25+
return () => {
26+
attrObserver.disconnect()
27+
}
28+
})
29+
30+
return isDark
31+
}

docs/.vitepress/theme/layout.vue

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
<script setup lang="ts">
2-
import { useData } from 'vitepress'
32
import DefaultTheme from 'vitepress/theme'
43
import { nextTick, provide } from 'vue'
54
5+
import { useData } from 'vitepress'
6+
import useDark from '../../../components/midori/use-dark'
67
import Ray from '../../../components/midori/ray.vue'
78
8-
const { isDark } = useData()
9+
const isDark = useDark()
10+
const { isDark: darkTheme } = useData()
911
1012
const enableTransitions = () =>
1113
'startViewTransition' in document &&
1214
window.matchMedia('(prefers-reduced-motion: no-preference)').matches
1315
1416
provide('toggle-appearance', async ({ clientX: x, clientY: y }: MouseEvent) => {
1517
if (!enableTransitions()) {
16-
isDark.value = !isDark.value
18+
darkTheme.value = !darkTheme.value
1719
return
1820
}
1921
@@ -27,7 +29,7 @@ provide('toggle-appearance', async ({ clientX: x, clientY: y }: MouseEvent) => {
2729
2830
// @ts-ignore
2931
await document.startViewTransition(async () => {
30-
isDark.value = !isDark.value
32+
darkTheme.value = !darkTheme.value
3133
await nextTick()
3234
}).ready
3335

docs/essential/life-cycle.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ Elysia does the following for every request:
5454
4. **Validation** (not interceptable)
5555
- Strictly validate incoming request provided by `Elysia.t`
5656
5. **Before Handle**
57-
- Execute right before the route handler
57+
- Custom validation before route handler
5858
- **If value is returned, route handler will be skipped**
5959
- Best for:
6060
- Providing custom requirements to access route, eg. user session, authorization.

docs/life-cycle/overview.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,32 +15,32 @@ Below are the request lifecycle available in Elysia:
1515

1616
<Deck>
1717
<Card title="Request" href="request">
18-
Hello World
18+
Notify new event is received
1919
</Card>
2020
<Card title="Parse" href="parse">
21-
Hello World
21+
Parse body into <b>Context.body</b>
2222
</Card>
2323
<Card title="Transform" href="transform">
24-
Hello World
24+
Modify <b>Context</b> before validation
2525
</Card>
2626
<Card title="Before Handle" href="before-handle">
27-
Hello World
27+
Custom validation before route handler
2828
</Card>
2929
<Card title="After Handle" href="after-handle">
30-
Hello World
30+
Map returned value into a response
3131
</Card>
3232
<Card title="On Error" href="on-error">
33-
Hello World
33+
Capture error when thrown
3434
</Card>
3535
<Card title="On Response" href="on-response">
36-
Hello World
36+
Executed after response sent to the client
37+
</Card>
38+
<Card title="Trace" href="trace">
39+
Audit and capture timespan of each event
3740
</Card>
3841
</Deck>
3942

40-
Below are the additional life-cycle event available in Elysia:
41-
1. Trace
42-
2. Start
43-
3. Stop
43+
---
4444

4545
Every life-cycle could be apply at both:
4646
1. Local Hook (route)

docs/life-cycle/parse.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,66 @@ new Elysia()
2222
})
2323
```
2424

25+
The returned value will be assigned to Context.body. If not, Elysia will continue iterating through additional parser functions from **onParse** stack until either body is assigned or all parsers have been executed.
26+
2527
## Context
2628
`onParse` Context is extends from `Context` with additional properties of the following:
2729
- contentType: Content-Type header of the request
2830

2931
All of the context is based on normal context and can be used like normal context in route handler.
32+
33+
## Explicit Body
34+
35+
By default, Elysia will try to determine body parsing function ahead of time and pick the most suitable function to speed up the process.
36+
37+
Elysia is able to determine that body function by reading `body`.
38+
39+
Take a look at this example:
40+
```ts
41+
app.post('/', ({ body }) => body, {
42+
body: t.Object({
43+
username: t.String(),
44+
password: t.String()
45+
})
46+
})
47+
```
48+
49+
Elysia read the body schema and found that, the type is entirely an object, so it's likely that the body will be JSON. Elysia then picks the JSON body parser function ahead of time and tries to parse the body.
50+
51+
Here's a criteria that Elysia uses to pick up type of body parser
52+
53+
- `application/json`: body typed as `t.Object`
54+
- `multipart/form-data`: body typed as `t.Object`, and is 1 level deep with `t.File`
55+
- `application/x-www-form-urlencoded`: body typed as `t.URLEncoded`
56+
- `text/plain`: other primitive type
57+
58+
This allows Elysia to optimize body parser ahead of time, and reduce overhead in compile time.
59+
60+
## Explicit Content Type
61+
However, in some scenario if Elysia fails to pick the correct body parser function, we can explicitly tell Elysia to use a certain function by specifying `type`
62+
63+
```ts
64+
app.post('/', ({ body }) => body, {
65+
// Short form of application/json
66+
type: 'json',
67+
})
68+
```
69+
70+
Allowing us to control Elysia behavior for picking body parser function to fit our needs in a complex scenario.
71+
72+
`type` may be one of the following:
73+
```ts
74+
type ContentType = |
75+
// Shorthand for 'text/plain'
76+
| 'text'
77+
// Shorthand for 'application/json'
78+
| 'json'
79+
// Shorthand for 'multipart/form-data'
80+
| 'formdata'
81+
// Shorthand for 'application/x-www-form-urlencoded'\
82+
| 'urlencoded'
83+
| 'text/plain'
84+
| 'application/json'
85+
| 'multipart/form-data'
86+
| 'application/x-www-form-urlencoded'
87+
```

docs/validation/schema-type.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ Elysia supports declarative schema with the following types:
3030

3131
---
3232

33-
These properties should be used in route `hook` and `guard`, Elysia then will validate the request automatically:
33+
These properties should be provided as the third argument of the route handler to validate the incoming request.
3434

3535
```typescript
3636
import { Elysia, t } from 'elysia'

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,15 @@
99
"license": "MIT",
1010
"dependencies": {
1111
"@iconify/json": "^2.2.145",
12+
"@tailwindcss/nesting": "0.0.0-insiders.565cd3e",
1213
"@tailwindcss/postcss7-compat": "^2.2.17",
1314
"autoprefixer": "^10.4.16",
1415
"daisyui": "^4.4.2",
1516
"elysia": "^0.7.29",
1617
"openai": "^4.19.1",
1718
"postcss": "^8.4.31",
19+
"postcss-nesting": "^12.0.1",
20+
"postcss-preset-env": "^9.3.0",
1821
"prismjs": "^1.29.0",
1922
"tailwindcss": "^3.3.5",
2023
"vite": "^5.0.2",

0 commit comments

Comments
 (0)