Skip to content

Commit 7e6fee5

Browse files
author
tkokhing
committed
improved UI of toggleframe with single Expand / Collapse All for faster nav
1 parent 0cc5d6d commit 7e6fee5

11 files changed

Lines changed: 196 additions & 62 deletions

File tree

src/app/_components/language_handler/language.module.css

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,10 @@
44
--spacing: 20px;
55
bottom: calc(3vw + var(--spacing));
66
left: calc(var(--spacing) * 100%);
7-
zIndex: 1;
7+
z-index: 1;
88
border-radius: 50%;
9-
background-color: red;
10-
border: 2px solid black;
119
font-size: 10px;
12-
--size: 30px;
10+
--size: 50px;
1311
width: var(--size);
1412
height: var(--size);
1513
padding: 0;
@@ -45,10 +43,12 @@
4543
[data-mode="light"] .languageSwitch {
4644
border: 2px solid grey;
4745
background-color: yellow;
46+
color: black;
4847
}
4948

5049
[data-mode="light"] .languageSwitch:hover {
5150
border: 1.5px dashed black;
5251
background-color: white;
5352
box-shadow: 0 0 20px 0px orange;
53+
color: black;
5454
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// ToggleAllButton.tsx || toggle-frame-button.tsx
2+
"use client";
3+
4+
import { useState } from "react";
5+
import { useToggleBroadcast } from "./toggle-frame-provider";
6+
7+
export function ToggleAllButton() {
8+
const ctx = useToggleBroadcast();
9+
const [state, setState] = useState<"open" | "closed">("open");
10+
11+
if (!ctx) return null;
12+
13+
return (
14+
<div className="sticky top-[4.5rem] z-20">
15+
<div className="flex gap-2 px-4 py-2 justify-end">
16+
<button
17+
disabled={state === "open"}
18+
onClick={() => {
19+
ctx.openAll();
20+
setState("open");
21+
}}
22+
className={`px-3 py-1 rounded text-xs transition ${
23+
state === "open"
24+
? "bg-gray-700 dark:bg-gray-600 text-tkokhing-blue cursor-default"
25+
: "bg-gray-200 hover:bg-gray-500 dark:text-tkokhing-blue hover:text-tkokhing-dark hover:dark:text-tkokhing-dark"
26+
}`}
27+
>
28+
Expand All
29+
</button>
30+
31+
<button
32+
disabled={state === "closed"}
33+
onClick={() => {
34+
ctx.closeAll();
35+
setState("closed");
36+
}}
37+
className={`px-3 py-1 rounded text-xs transition ${
38+
state === "closed"
39+
? "bg-gray-700 dark:bg-gray-600 text-tkokhing-blue cursor-default"
40+
: "bg-gray-200 hover:bg-gray-500 dark:text-tkokhing-blue hover:text-tkokhing-dark hover:dark:text-tkokhing-dark"
41+
}`}
42+
>
43+
Collapse All
44+
</button>
45+
</div>
46+
</div>
47+
);
48+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// components/ToggleAllFrame.tsx
2+
"use client";
3+
4+
import { ToggleAllButton } from "./toggle-frame-button";
5+
6+
export function ToggleAllFrame({ children }: { children: React.ReactNode }) {
7+
return (
8+
<>
9+
<ToggleAllButton />
10+
{children}
11+
</>
12+
);
13+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// ToggleBroadcastContext.tsx // ToggleFrameContext.tsx || toggle-frame-provider.tsx
2+
3+
"use client";
4+
5+
import { createContext, useContext, useRef } from "react";
6+
7+
type ToggleHandler = (open: boolean) => void;
8+
9+
type ToggleContextType = {
10+
openAll: () => void;
11+
closeAll: () => void;
12+
register: (handler: ToggleHandler) => () => void;
13+
};
14+
15+
const ToggleContext = createContext<ToggleContextType | null>(null);
16+
17+
export function ToggleFrameProvider({
18+
children,
19+
}: {
20+
children: React.ReactNode;
21+
}) {
22+
const handlersRef = useRef<Set<ToggleHandler>>(new Set());
23+
24+
const register = (handler: ToggleHandler) => {
25+
handlersRef.current.add(handler);
26+
return () => handlersRef.current.delete(handler);
27+
};
28+
29+
const openAll = () => {
30+
handlersRef.current.forEach((h) => h(true));
31+
};
32+
33+
const closeAll = () => {
34+
handlersRef.current.forEach((h) => h(false));
35+
};
36+
37+
return (
38+
<ToggleContext.Provider value={{ openAll, closeAll, register }}>
39+
{children}
40+
</ToggleContext.Provider>
41+
);
42+
}
43+
44+
export const useToggleBroadcast = () => useContext(ToggleContext);

src/app/_components/preference/toggle-frame.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,34 @@
11
"use client";
22

33
import { useRef, useState, useEffect } from "react";
4+
import { useToggleBroadcast } from "./toggle-frame-provider";
45

56
type Props = {
67
label: string;
78
children: React.ReactNode;
89
defaultOpen?: boolean;
910
};
1011

12+
13+
// Next 2 lines are switching between "defaultOpen = true" Vs "defaultOpen = false" for quick open_all, close_all
1114
export function ToggleFrame({ label, children, defaultOpen = true }: Props) {
1215
// export function ToggleFrame({ label, children, defaultOpen = false }: Props) {
16+
17+
const ctx = useToggleBroadcast();
1318
const [isOpen, setIsOpen] = useState(defaultOpen);
1419
const ref = useRef<HTMLDivElement>(null);
1520

1621
const handleToggle = () => {
1722
setIsOpen((prev) => !prev);
1823
};
24+
// for Toggle open / closed
25+
useEffect(() => {
26+
if (!ctx) return;
27+
28+
return ctx.register((open: boolean) => {
29+
setIsOpen(open);
30+
});
31+
}, [ctx]);
1932

2033
// Scroll into view when ESC is pressed
2134
useEffect(() => {

src/app/blog/posts/[slug]/page.tsx

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { PostBody } from "@/app/_components/post_gen/post-body";
1010
import { FrontierData } from "@/lib/_data_exporter/data_securingdigitalfrontiers/frontier_data-exporter";
1111
import { FrontierLeadinData } from "@/lib/_data_exporter/data_securingdigitalfrontiers/frontier_data-exporter";
1212
import { ToggleFrame } from "@/app/_components/preference/toggle-frame";
13+
import { ToggleAllFrame } from "@/app/_components/preference/toggle-frame-display";
1314
import { PostListConcise } from "@/app/_components/post_gen/post-list-concise";
1415

1516
const MDX_FOLDER = "_blog_post/_blogs";
@@ -34,17 +35,19 @@ export default async function Post(props: Params) {
3435
return (
3536
<main>
3637
<Container>
37-
<article className="mb-32">
38-
<PostHeader
39-
title={post.title}
40-
coverImage={post.coverImage}
41-
date={post.date}
42-
author={post.author}
43-
subPath={post.subPath}
44-
postStatus={post.postStatus}
45-
/>
46-
<PostBody content={post.content} components={ImportComponents}/>
47-
</article>
38+
<ToggleAllFrame>
39+
<article className="mb-32">
40+
<PostHeader
41+
title={post.title}
42+
coverImage={post.coverImage}
43+
date={post.date}
44+
author={post.author}
45+
subPath={post.subPath}
46+
postStatus={post.postStatus}
47+
/>
48+
<PostBody content={post.content} components={ImportComponents}/>
49+
</article>
50+
</ToggleAllFrame>
4851
</Container>
4952
</main>
5053
);

src/app/heptagoning/kill-chain/[slug]/page.tsx

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { getPostBySlug } from "@/lib/share/api";
88
import { generatePageMetadata } from "@/lib/share/generatePageMetadata";
99
import { generatePageStaticParams } from "@/lib/share/generatePageStaticParams";
1010
import { ToggleFrame } from "@/app/_components/preference/toggle-frame";
11+
import { ToggleAllFrame } from "@/app/_components/preference/toggle-frame-display";
1112
import { PostListConcise } from "@/app/_components/post_gen/post-list-concise";
1213

1314
const MDX_FOLDER = "_heptagoning/_kill-chain";
@@ -30,17 +31,19 @@ export default async function Post(props: Params) {
3031
return (
3132
<main>
3233
<Container>
33-
<article className="mb-32">
34-
<PostHeader
35-
title={post.title}
36-
coverImage={post.coverImage}
37-
date={post.date}
38-
author={post.author}
39-
subPath={post.subPath}
40-
postStatus={post.postStatus}
41-
/>
42-
<PostBody content={post.content} components={ImportComponents}/>
43-
</article>
34+
<ToggleAllFrame>
35+
<article className="mb-32">
36+
<PostHeader
37+
title={post.title}
38+
coverImage={post.coverImage}
39+
date={post.date}
40+
author={post.author}
41+
subPath={post.subPath}
42+
postStatus={post.postStatus}
43+
/>
44+
<PostBody content={post.content} components={ImportComponents}/>
45+
</article>
46+
</ToggleAllFrame>
4447
</Container>
4548
</main>
4649
);

src/app/heptagoning/kill-chain/active_dir/[slug]/page.tsx

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import Alert from "@/app/_components/blog_frame/alert";
1212
import Note from "@/app/_components/blog_frame/note";
1313
import Tip from "@/app/_components/blog_frame/tip";
1414
import { ToggleFrame } from "@/app/_components/preference/toggle-frame";
15-
15+
import { ToggleAllFrame } from "@/app/_components/preference/toggle-frame-display";
1616
const MDX_FOLDER = "_heptagoning/_kill-chain/_active_dir";
1717

1818
type Params = {
@@ -52,17 +52,19 @@ export default async function Post(props: Params) {
5252
return (
5353
<main>
5454
<Container>
55-
<article className="mb-32">
56-
<PostHeader
57-
title={post.title}
58-
coverImage={post.coverImage}
59-
date={post.date}
60-
author={post.author}
61-
subPath={post.subPath}
62-
postStatus={post.postStatus}
63-
/>
64-
<PostBody content={post.content} components={ImportComponents}/>
65-
</article>
55+
<ToggleAllFrame>
56+
<article className="mb-32">
57+
<PostHeader
58+
title={post.title}
59+
coverImage={post.coverImage}
60+
date={post.date}
61+
author={post.author}
62+
subPath={post.subPath}
63+
postStatus={post.postStatus}
64+
/>
65+
<PostBody content={post.content} components={ImportComponents}/>
66+
</article>
67+
</ToggleAllFrame>
6668
</Container>
6769
</main>
6870
);

src/app/heptagoning/kill-chain/delivery/[slug]/page.tsx

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import Alert from "@/app/_components/blog_frame/alert";
1212
import Note from "@/app/_components/blog_frame/note";
1313
import Tip from "@/app/_components/blog_frame/tip";
1414
import { ToggleFrame } from "@/app/_components/preference/toggle-frame";
15-
15+
import { ToggleAllFrame } from "@/app/_components/preference/toggle-frame-display";
1616
const MDX_FOLDER = "_heptagoning/_kill-chain/_delivery";
1717

1818
type Params = {
@@ -39,17 +39,19 @@ export default async function Post(props: Params) {
3939
return (
4040
<main>
4141
<Container>
42-
<article className="mb-32">
43-
<PostHeader
44-
title={post.title}
45-
coverImage={post.coverImage}
46-
date={post.date}
47-
author={post.author}
48-
subPath={post.subPath}
49-
postStatus={post.postStatus}
50-
/>
51-
<PostBody content={post.content} components={ImportComponents}/>
52-
</article>
42+
<ToggleAllFrame>
43+
<article className="mb-32">
44+
<PostHeader
45+
title={post.title}
46+
coverImage={post.coverImage}
47+
date={post.date}
48+
author={post.author}
49+
subPath={post.subPath}
50+
postStatus={post.postStatus}
51+
/>
52+
<PostBody content={post.content} components={ImportComponents}/>
53+
</article>
54+
</ToggleAllFrame>
5355
</Container>
5456
</main>
5557
);

src/app/heptagoning/kill-chain/recon/[slug]/page.tsx

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import Alert from "@/app/_components/blog_frame/alert";
1212
import Note from "@/app/_components/blog_frame/note";
1313
import Tip from "@/app/_components/blog_frame/tip";
1414
import { ToggleFrame } from "@/app/_components/preference/toggle-frame";
15+
import { ToggleAllFrame } from "@/app/_components/preference/toggle-frame-display";
1516

1617
const MDX_FOLDER = "_heptagoning/_kill-chain/_recon";
1718

@@ -53,17 +54,19 @@ export default async function Post(props: Params) {
5354
return (
5455
<main>
5556
<Container>
56-
<article className="mb-32">
57-
<PostHeader
58-
title={post.title}
59-
coverImage={post.coverImage}
60-
date={post.date}
61-
author={post.author}
62-
subPath={post.subPath}
63-
postStatus={post.postStatus}
64-
/>
65-
<PostBody content={post.content} components={ImportComponents}/>
66-
</article>
57+
<ToggleAllFrame>
58+
<article className="mb-32">
59+
<PostHeader
60+
title={post.title}
61+
coverImage={post.coverImage}
62+
date={post.date}
63+
author={post.author}
64+
subPath={post.subPath}
65+
postStatus={post.postStatus}
66+
/>
67+
<PostBody content={post.content} components={ImportComponents}/>
68+
</article>
69+
</ToggleAllFrame>
6770
</Container>
6871
</main>
6972
);

0 commit comments

Comments
 (0)