tangled
alpha
login
or
join now
vielle.dev
/
pdsls
forked from
pds.ls/pdsls
0
fork
atom
atproto explorer
0
fork
atom
overview
issues
pulls
pipelines
sticky component
handle.invalid
6 months ago
93ef3f86
37b2965c
+51
-49
3 changed files
expand all
collapse all
unified
split
src
components
sticky.tsx
views
collection.tsx
labels.tsx
+44
src/components/sticky.tsx
···
1
1
+
import { createSignal, JSX, onCleanup, onMount } from "solid-js";
2
2
+
3
3
+
export const StickyOverlay = (props: { children?: JSX.Element }) => {
4
4
+
const [filterStuck, setFilterStuck] = createSignal(false);
5
5
+
6
6
+
return (
7
7
+
<div
8
8
+
ref={(node) => {
9
9
+
onMount(() => {
10
10
+
let ticking = false;
11
11
+
const tick = () => {
12
12
+
const topPx = parseFloat(getComputedStyle(node).top);
13
13
+
const { top } = node.getBoundingClientRect();
14
14
+
setFilterStuck(top <= topPx + 0.5);
15
15
+
ticking = false;
16
16
+
};
17
17
+
18
18
+
const onScroll = () => {
19
19
+
if (!ticking) {
20
20
+
ticking = true;
21
21
+
requestAnimationFrame(tick);
22
22
+
}
23
23
+
};
24
24
+
25
25
+
window.addEventListener("scroll", onScroll, { passive: true });
26
26
+
27
27
+
tick();
28
28
+
29
29
+
onCleanup(() => {
30
30
+
window.removeEventListener("scroll", onScroll);
31
31
+
});
32
32
+
});
33
33
+
}}
34
34
+
class="sticky top-2 z-10 flex flex-col items-center justify-center gap-2 rounded-lg p-3 transition-colors"
35
35
+
classList={{
36
36
+
"bg-neutral-50 dark:bg-dark-300 border-[0.5px] border-neutral-300 dark:border-neutral-700 shadow-md":
37
37
+
filterStuck(),
38
38
+
"bg-transparent border-transparent shadow-none": !filterStuck(),
39
39
+
}}
40
40
+
>
41
41
+
{props.children}
42
42
+
</div>
43
43
+
);
44
44
+
};
+4
-47
src/views/collection.tsx
···
3
3
import { $type, ActorIdentifier, InferXRPCBodyOutput } from "@atcute/lexicons";
4
4
import * as TID from "@atcute/tid";
5
5
import { A, useParams } from "@solidjs/router";
6
6
-
import {
7
7
-
createEffect,
8
8
-
createResource,
9
9
-
createSignal,
10
10
-
For,
11
11
-
onCleanup,
12
12
-
onMount,
13
13
-
Show,
14
14
-
untrack,
15
15
-
} from "solid-js";
6
6
+
import { createEffect, createResource, createSignal, For, Show, untrack } from "solid-js";
16
7
import { createStore } from "solid-js/store";
17
8
import { Button } from "../components/button.jsx";
18
9
import { JSONType, JSONValue } from "../components/json.jsx";
19
10
import { agent } from "../components/login.jsx";
11
11
+
import { StickyOverlay } from "../components/sticky.jsx";
20
12
import { TextInput } from "../components/text-input.jsx";
21
13
import Tooltip from "../components/tooltip.jsx";
22
14
import { setNotif } from "../layout.jsx";
···
81
73
const [batchDelete, setBatchDelete] = createSignal(false);
82
74
const [lastSelected, setLastSelected] = createSignal<number>();
83
75
const [reverse, setReverse] = createSignal(false);
84
84
-
const [filterStuck, setFilterStuck] = createSignal(false);
85
76
const did = params.repo;
86
77
let pds: string;
87
78
let rpc: Client;
88
88
-
let sticky!: HTMLDivElement;
89
79
90
80
const fetchRecords = async () => {
91
81
if (!pds) pds = await resolvePDS(did);
···
168
158
true,
169
159
);
170
160
171
171
-
onMount(() => {
172
172
-
let ticking = false;
173
173
-
const tick = () => {
174
174
-
const topPx = parseFloat(getComputedStyle(sticky).top);
175
175
-
const { top } = sticky.getBoundingClientRect();
176
176
-
setFilterStuck(top <= topPx + 0.5);
177
177
-
ticking = false;
178
178
-
};
179
179
-
180
180
-
const onScroll = () => {
181
181
-
if (!ticking) {
182
182
-
ticking = true;
183
183
-
requestAnimationFrame(tick);
184
184
-
}
185
185
-
};
186
186
-
187
187
-
window.addEventListener("scroll", onScroll, { passive: true });
188
188
-
189
189
-
tick();
190
190
-
191
191
-
onCleanup(() => {
192
192
-
window.removeEventListener("scroll", onScroll);
193
193
-
});
194
194
-
});
195
195
-
196
161
return (
197
162
<Show when={records.length || response()}>
198
163
<div class="-mt-2 flex w-full flex-col items-center">
199
199
-
<div
200
200
-
ref={(el) => (sticky = el)}
201
201
-
class="sticky top-2 z-10 flex flex-col items-center justify-center gap-2 rounded-lg p-3 transition-colors"
202
202
-
classList={{
203
203
-
"bg-neutral-50 dark:bg-dark-300 border-[0.5px] border-neutral-300 dark:border-neutral-700 shadow-md":
204
204
-
filterStuck(),
205
205
-
"bg-transparent border-transparent shadow-none": !filterStuck(),
206
206
-
}}
207
207
-
>
164
164
+
<StickyOverlay>
208
165
<div class="flex w-[22rem] items-center gap-2 sm:w-[24rem]">
209
166
<Show when={agent() && agent()?.sub === did}>
210
167
<div class="flex items-center gap-x-2">
···
297
254
</div>
298
255
</div>
299
256
</Show>
300
300
-
</div>
257
257
+
</StickyOverlay>
301
258
<div class="flex max-w-full flex-col font-mono">
302
259
<For
303
260
each={records.filter((rec) =>
+3
-2
src/views/labels.tsx
···
3
3
import { A, useParams, useSearchParams } from "@solidjs/router";
4
4
import { createResource, createSignal, For, onMount, Show } from "solid-js";
5
5
import { Button } from "../components/button.jsx";
6
6
+
import { StickyOverlay } from "../components/sticky.jsx";
6
7
import { TextInput } from "../components/text-input.jsx";
7
8
import { labelerCache, resolvePDS } from "../utils/api.js";
8
9
import { localDateFromTimestamp } from "../utils/date.js";
···
98
99
</div>
99
100
</div>
100
101
</form>
101
101
-
<div class="dark:bg-dark-500 sticky top-0 z-5 flex w-screen flex-col items-center justify-center gap-3 bg-neutral-100 py-3">
102
102
+
<StickyOverlay>
102
103
<TextInput
103
104
placeholder="Filter by label"
104
105
onInput={(e) => setFilter(e.currentTarget.value)}
···
123
124
</div>
124
125
</Show>
125
126
</div>
126
126
-
</div>
127
127
+
</StickyOverlay>
127
128
<Show when={labels().length}>
128
129
<div class="flex max-w-full min-w-[22rem] flex-col gap-2 divide-y-[0.5px] divide-neutral-400 text-sm wrap-anywhere whitespace-pre-wrap sm:min-w-[24rem] dark:divide-neutral-600">
129
130
<For each={filterLabels()}>