Skip to content

Commit df2bb23

Browse files
authored
feat: search and backlink compatability (#8286)
* Copy the right files * Finish search * FIx accessibility issues * Add original site compatibility back in * Remove console.log * Reorganize imports * Minor refactor * Fix undefined heading issue * Replace state on redirect * Don't redirect to docs/introduction from navbar * Cleanup search * Cleanup some more(html entities) * Remove console log * Minor style tweaks * Put search in middle
1 parent 1b12546 commit df2bb23

File tree

25 files changed

+1370
-53
lines changed

25 files changed

+1370
-53
lines changed

sites/svelte.dev/package-lock.json

Lines changed: 31 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sites/svelte.dev/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@
2121
"cookie": "^0.5.0",
2222
"devalue": "^4.3.0",
2323
"do-not-zip": "^1.0.0",
24+
"flexsearch": "^0.7.31",
2425
"flru": "^1.0.2",
25-
"sourcemap-codec": "^1.4.8"
26+
"sourcemap-codec": "^1.4.8",
27+
"svelte-local-storage-store": "^0.4.0"
2628
},
2729
"devDependencies": {
2830
"@resvg/resvg-js": "^2.4.0",
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/** @param {HTMLElement} node */
2+
export function focusable_children(node) {
3+
const nodes = Array.from(
4+
node.querySelectorAll(
5+
'a[href], button, input, textarea, select, details, [tabindex]:not([tabindex="-1"])'
6+
)
7+
);
8+
9+
const index = nodes.indexOf(document.activeElement);
10+
11+
const update = (d) => {
12+
let i = index + d;
13+
i += nodes.length;
14+
i %= nodes.length;
15+
16+
// @ts-expect-error
17+
nodes[i].focus();
18+
};
19+
20+
return {
21+
/** @param {string} [selector] */
22+
next: (selector) => {
23+
const reordered = [...nodes.slice(index + 1), ...nodes.slice(0, index + 1)];
24+
25+
for (let i = 0; i < reordered.length; i += 1) {
26+
if (!selector || reordered[i].matches(selector)) {
27+
reordered[i].focus();
28+
return;
29+
}
30+
}
31+
},
32+
/** @param {string} [selector] */
33+
prev: (selector) => {
34+
const reordered = [...nodes.slice(index + 1), ...nodes.slice(0, index + 1)];
35+
36+
for (let i = reordered.length - 2; i >= 0; i -= 1) {
37+
if (!selector || reordered[i].matches(selector)) {
38+
reordered[i].focus();
39+
return;
40+
}
41+
}
42+
},
43+
update
44+
};
45+
}
46+
47+
export function trap(node) {
48+
const handle_keydown = (e) => {
49+
if (e.key === 'Tab') {
50+
e.preventDefault();
51+
52+
const group = focusable_children(node);
53+
if (e.shiftKey) {
54+
group.prev();
55+
} else {
56+
group.next();
57+
}
58+
}
59+
};
60+
61+
node.addEventListener('keydown', handle_keydown);
62+
63+
return {
64+
destroy: () => {
65+
node.removeEventListener('keydown', handle_keydown);
66+
}
67+
};
68+
}
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
<script>
2+
import { browser } from '$app/environment';
3+
import { searching, query } from './stores.js';
4+
5+
export let q = '';
6+
</script>
7+
8+
<form class="search-container" action="/search">
9+
<input
10+
value={q}
11+
on:input={(e) => {
12+
$searching = true;
13+
$query = e.currentTarget.value;
14+
e.currentTarget.value = '';
15+
}}
16+
on:mousedown|preventDefault={() => ($searching = true)}
17+
on:touchend|preventDefault={() => ($searching = true)}
18+
type="search"
19+
name="q"
20+
placeholder="Search"
21+
aria-label="Search"
22+
spellcheck="false"
23+
/>
24+
25+
{#if browser}
26+
<div class="shortcut">
27+
<kbd>{navigator.platform === 'MacIntel' ? '' : 'Ctrl'}</kbd> <kbd>K</kbd>
28+
</div>
29+
{/if}
30+
</form>
31+
32+
<style>
33+
@keyframes fade-in {
34+
from {
35+
opacity: 0;
36+
}
37+
to {
38+
opacity: 1;
39+
}
40+
}
41+
42+
.search-container {
43+
position: relative;
44+
display: flex;
45+
align-items: center;
46+
width: 100%;
47+
height: 100%;
48+
}
49+
50+
input {
51+
padding: 0.5em 0.5em 0.4em 2em;
52+
border: 1px solid var(--sk-back-translucent);
53+
font-family: inherit;
54+
font-size: 1.4rem;
55+
/* text-align: center; */
56+
appearance: none;
57+
-webkit-appearance: none;
58+
width: 100%;
59+
height: 3.2rem;
60+
border-radius: var(--sk-border-radius);
61+
background: no-repeat 1rem 50% / 1em 1em url(/icons/search.svg);
62+
color: var(--sk-text-3);
63+
}
64+
65+
input:focus + .shortcut {
66+
display: none;
67+
}
68+
69+
input::placeholder {
70+
font-size: 1.2rem;
71+
text-transform: uppercase;
72+
}
73+
74+
.shortcut {
75+
color: var(--sk-text-3);
76+
position: absolute;
77+
top: calc(50% - 0.9rem);
78+
right: 0;
79+
width: 100%;
80+
text-align: right;
81+
pointer-events: none;
82+
font-size: 1.2rem;
83+
text-transform: uppercase;
84+
animation: fade-in 0.2s;
85+
}
86+
87+
kbd {
88+
display: none;
89+
background: var(--sk-back-2);
90+
border: 1px solid var(--sk-back-translucent);
91+
padding: 0.2rem 0.2rem 0rem 0.2rem;
92+
color: var(--sk-text-3);
93+
font-size: inherit;
94+
font-family: inherit;
95+
border-radius: 2px;
96+
}
97+
98+
@media (min-width: 800px) {
99+
.search-container {
100+
width: 11rem;
101+
}
102+
103+
.shortcut {
104+
padding: 0 1.6rem 0 0;
105+
}
106+
107+
input {
108+
border-radius: 1.6rem;
109+
}
110+
111+
input::placeholder {
112+
opacity: 0;
113+
}
114+
115+
/* we're using media query as an imperfect proxy for mobile/desktop */
116+
kbd {
117+
display: inline;
118+
}
119+
}
120+
121+
@media (min-width: 960px) {
122+
.search-container {
123+
width: 19rem;
124+
}
125+
126+
input::placeholder {
127+
opacity: 1;
128+
}
129+
}
130+
</style>

0 commit comments

Comments
 (0)