Skip to content

Commit df106c6

Browse files
authored
Merge pull request #56 from hapijs/chore/outline-improvements
chore: add some outlines improvements
2 parents 38e89cf + 815f520 commit df106c6

11 files changed

Lines changed: 112 additions & 56 deletions

File tree

.oxlintrc.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
},
1616
"globals": {
1717
"defineProps": "readonly",
18-
"defineEmits": "readonly"
18+
"defineModel": "readonly"
1919
},
2020
"rules": {
2121
"id-length": "off",

.vitepress/theme/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { h } from 'vue';
44
import './variables.css';
55
import './main.css';
66
import 'virtual:group-icons.css';
7+
import ApiOutline from '../../components/ApiOutline.vue';
78
import CarbonAds from '../../components/CarbonAds.vue';
89
import ModuleIndex from '../../components/ModuleIndex.vue';
910
import StatusContent from '../../components/StatusContent.vue';
@@ -22,6 +23,7 @@ export default {
2223
app.component('ModuleIndex', ModuleIndex);
2324
app.component('StatusContent', StatusContent);
2425
app.component('TesterContent', TesterContent);
26+
app.component('ApiOutline', ApiOutline);
2527

2628
if (typeof window !== 'undefined') {
2729
router.onBeforeRouteChange = (to) => {

components/ApiOutline.vue

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<script setup>
2+
import { useMutationObserver } from '@vueuse/core';
3+
import { onMounted, ref } from 'vue';
4+
5+
const outline = ref(null);
6+
7+
8+
const onActiveChange = (mutations) => {
9+
for (const { target, type, attributeName } of mutations) {
10+
if (
11+
type === 'attributes' &&
12+
attributeName === 'class' &&
13+
target.classList.contains('active') &&
14+
target.classList.contains('outline-link')
15+
) {
16+
target.scrollIntoView({ behavior: 'smooth', block: 'center' });
17+
}
18+
}
19+
};
20+
21+
22+
onMounted(() => {
23+
outline.value = document.querySelector('.VPDocAsideOutline');
24+
});
25+
26+
27+
useMutationObserver(outline, onActiveChange, {
28+
attributeFilter: ['class'],
29+
subtree: true,
30+
});
31+
</script>
32+
33+
<template>
34+
<span class="api-outline-marker" />
35+
</template>
36+
37+
<style>
38+
.api-outline-marker {
39+
display: none;
40+
}
41+
42+
.VPDoc:has(.api-outline-marker) .VPDocAsideOutline .outline-link + ul {
43+
display: none;
44+
}
45+
46+
.VPDoc:has(.api-outline-marker) .VPDocAsideOutline li:has(.outline-link.active) > ul,
47+
.VPDoc:has(.api-outline-marker) .VPDocAsideOutline li.expanded > ul {
48+
display: block;
49+
}
50+
51+
.VPDoc:has(.api-outline-marker) .VPDocAsideOutline .outline-link {
52+
display: flex;
53+
line-height: 28px;
54+
position: relative;
55+
}
56+
57+
.VPDoc:has(.api-outline-marker) .VPDocAsideOutline .outline-link::before {
58+
content: '';
59+
width: 15px;
60+
flex-shrink: 0;
61+
}
62+
63+
.VPDoc:has(.api-outline-marker) .VPDocAsideOutline li:has(> ul) > .outline-link::before {
64+
width: 15px;
65+
height: 30px;
66+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 20 20' fill='none' stroke='%230080ff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='8 4 14 10 8 16'/%3E%3C/svg%3E");
67+
background-repeat: no-repeat;
68+
background-position: center;
69+
cursor: pointer;
70+
transition: transform 0.3s ease-in-out;
71+
}
72+
73+
.VPDoc:has(.api-outline-marker) .VPDocAsideOutline li:has(> ul) > .outline-link {
74+
color: var(--vp-c-brand-1);
75+
}
76+
77+
.VPDoc:has(.api-outline-marker) .VPDocAsideOutline li:has(.outline-link.active) > .outline-link::before,
78+
.VPDoc:has(.api-outline-marker) .VPDocAsideOutline li.expanded > .outline-link::before {
79+
transform: rotate(90deg);
80+
}
81+
</style>

components/CodeMirrorEditor.vue

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,14 @@ import { EditorView, basicSetup } from 'codemirror';
1515
import { useData } from 'vitepress';
1616
import { onBeforeUnmount, onMounted, ref, watch } from 'vue';
1717
18-
const { errorLines, language, modelValue, readOnly } = defineProps({
18+
const { errorLines, language, readOnly } = defineProps({
1919
errorLines: { default: () => [], type: Array },
2020
language: { default: 'javascript', type: String },
21-
modelValue: { default: '', type: String },
2221
readOnly: { default: false, type: Boolean },
2322
});
2423
2524
26-
const emit = defineEmits(['update:modelValue']);
25+
const data = defineModel({ default: '', type: String });
2726
2827
2928
const { isDark } = useData();
@@ -81,7 +80,7 @@ onMounted(() => {
8180
themeCompartment.of(getThemeExtension()),
8281
EditorView.updateListener.of((update) => {
8382
if (update.docChanged) {
84-
emit('update:modelValue', update.state.doc.toString());
83+
data.value = update.state.doc.toString();
8584
}
8685
}),
8786
];
@@ -97,7 +96,7 @@ onMounted(() => {
9796
9897
view = new EditorView({
9998
parent: editorContainer.value,
100-
state: EditorState.create({ doc: modelValue, extensions }),
99+
state: EditorState.create({ doc: data.value, extensions }),
101100
});
102101
103102
if (readOnly && errorLines.length > 0) {
@@ -106,16 +105,13 @@ onMounted(() => {
106105
});
107106
108107
109-
watch(
110-
() => modelValue,
111-
(newValue) => {
112-
if (view && newValue !== view.state.doc.toString()) {
113-
view.dispatch({
114-
changes: { from: 0, insert: newValue, to: view.state.doc.length },
115-
});
116-
}
117-
},
118-
);
108+
watch(data, (newValue) => {
109+
if (view && newValue !== view.state.doc.toString()) {
110+
view.dispatch({
111+
changes: { from: 0, insert: newValue, to: view.state.doc.length },
112+
});
113+
}
114+
});
119115
120116
121117
watch(

components/TesterContent.vue

Lines changed: 5 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
</template>
7070

7171
<script setup>
72+
import { useClipboard, useStorage } from '@vueuse/core';
7273
import { useRoute } from 'vitepress';
7374
import { onBeforeUnmount, onMounted, ref, watch } from 'vue';
7475
@@ -97,8 +98,8 @@ const DEFAULT_VALIDATE = `//Insert data to validate here
9798
9899
99100
const route = useRoute();
100-
const schema = ref(DEFAULT_SCHEMA);
101-
const validate = ref(DEFAULT_VALIDATE);
101+
const schema = useStorage('joi-sandbox-schema', DEFAULT_SCHEMA);
102+
const validate = useStorage('joi-sandbox-validate', DEFAULT_VALIDATE);
102103
const result = ref('');
103104
const resultEditorContent = ref('');
104105
const errorDetails = ref([]);
@@ -109,7 +110,7 @@ const version = ref('');
109110
const joiInstance = ref(null);
110111
const isLoading = ref(false);
111112
const latestVersion = ref(joiInfo.versionsArray[0]);
112-
const isCopied = ref(false);
113+
const { copy, copied: isCopied } = useClipboard();
113114
const showResetConfirm = ref(false);
114115
115116
@@ -176,33 +177,12 @@ onMounted(async () => {
176177
validate.value = state.data;
177178
}
178179
} catch {}
179-
} else {
180-
// Check if we have saved state in localStorage
181-
const savedSchema = localStorage.getItem('joi-sandbox-schema');
182-
const savedValidate = localStorage.getItem('joi-sandbox-validate');
183-
if (savedSchema !== null) {
184-
schema.value = savedSchema;
185-
}
186-
if (savedValidate !== null) {
187-
validate.value = savedValidate;
188-
}
189180
}
190181
191182
await loadJoi(v);
192183
});
193184
194185
195-
onBeforeUnmount(() => {
196-
saveState();
197-
});
198-
199-
200-
const saveState = () => {
201-
localStorage.setItem('joi-sandbox-schema', schema.value);
202-
localStorage.setItem('joi-sandbox-validate', validate.value);
203-
};
204-
205-
206186
watch(
207187
() => route.path,
208188
async (newPath) => {
@@ -211,7 +191,6 @@ watch(
211191
if (testerMatch) {
212192
const v = testerMatch.at(1);
213193
if (v !== version.value) {
214-
saveState();
215194
await loadJoi(v);
216195
}
217196
}
@@ -227,10 +206,6 @@ const onValidateClick = () => {
227206
}
228207
229208
230-
// Save to localStorage whenever we validate
231-
saveState();
232-
233-
234209
try {
235210
// oxlint-disable-next-line no-new-func
236211
const dataToValidate = new Function(`"use strict";return (${validate.value})`)();
@@ -291,8 +266,6 @@ const cancelReset = () => {
291266
292267
const confirmReset = () => {
293268
showResetConfirm.value = false;
294-
localStorage.removeItem('joi-sandbox-schema');
295-
localStorage.removeItem('joi-sandbox-validate');
296269
297270
298271
schema.value = DEFAULT_SCHEMA;
@@ -307,10 +280,6 @@ const confirmReset = () => {
307280
308281
309282
const onShareClick = async () => {
310-
// Also save to localStorage when sharing
311-
saveState();
312-
313-
314283
const state = JSON.stringify({
315284
data: validate.value,
316285
schema: schema.value,
@@ -321,11 +290,7 @@ const onShareClick = async () => {
321290
const encoded = btoa(unescape(encodeURIComponent(state)));
322291
const url = new URL(window.location.href);
323292
url.hash = `share=${encoded}`;
324-
await navigator.clipboard.writeText(url.toString());
325-
isCopied.value = true;
326-
setTimeout(() => {
327-
isCopied.value = false;
328-
}, 2000);
293+
await copy(url.toString());
329294
} catch {}
330295
};
331296
</script>

docs/api/[version].md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
title: API
33
---
44

5+
<ApiOutline />
6+
57
# API v{{ $params.fullVersion }}
68

79
### Installation

docs/module/[name]/api/[version].md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
title: Module API
33
---
44

5+
<ApiOutline />
6+
57
# {{ $params.package }} API v{{ $params.fullVersion }}
68

79
::: code-group

docs/module/[name]/changelog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
title: Module changelog
33
---
44

5+
<ApiOutline />
6+
57
# Changelog
68

79
::: v-pre

docs/resources/changelog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
title: Changelog
33
---
44

5+
<ApiOutline />
6+
57
# Changelog
68

79
::: v-pre

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"@lezer/highlight": "^1.2.3",
2828
"@uiw/codemirror-theme-darcula": "^4.25.8",
2929
"@uiw/codemirror-theme-eclipse": "^4.25.8",
30+
"@vueuse/core": "^14.2.1",
3031
"codemirror": "^6.0.2",
3132
"es-toolkit": "^1.45.1",
3233
"joi-17": "npm:joi@17.13.3",

0 commit comments

Comments
 (0)