Skip to content

Commit c167ab5

Browse files
committed
Add 'navigator' component to page
1 parent 27fc15a commit c167ab5

9 files changed

Lines changed: 903 additions & 14 deletions

File tree

docs/navigator.mdx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,11 @@
22
title: Navigator
33
sidebar_position: 2
44
---
5+
6+
import Navigator from "@site/src/components/navigator";
7+
8+
# Pattern Navigator
9+
10+
Find the right design pattern for your needs with our interactive decision tree. Answer a few simple questions and we'll recommend the perfect pattern for your use case.
11+
12+
<Navigator />

logic-diagram.mermaid

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
flowchart TD
2-
Start["What do you need to do?"] --> Q1{"Are you creating<br/>new objects?"}
2+
Q1{"Are you creating<br/>new objects?"}
33

44
Q1 -->|Yes| Creational["Creational Patterns<br/>(Object Creation)"]
5-
Q1 -->|No| Q2{"Are you dealing with<br/>object composition<br/>or structure?"}
5+
Q1 -->|No| Q2{"Do you need to organize<br/>or structure existing objects?"}
66

77
Q2 -->|Yes| Structural["Structural Patterns<br/>(Object Composition)"]
88
Q2 -->|No| Behavioral["Behavioral Patterns<br/>(Object Interaction)"]
@@ -16,41 +16,41 @@ flowchart TD
1616
C3 -->|Yes| AbstractFactory["Abstract Factory<br/>(Family of objects)"]
1717
C3 -->|No| Builder["Builder<br/>(Step-by-step)"]
1818

19-
C2 -->|No| C4{"Need to clone<br/>existing objects?"}
19+
C2 -->|No| C4{"Do you need to clone<br/>existing objects?"}
2020
C4 -->|Yes| Prototype["Prototype<br/>(Clone objects)"]
2121
C4 -->|No| FactoryMethod["Factory Method<br/>(Encapsulate creation)"]
2222

2323
%% Structural Branch
2424
Structural --> S1{"Are you wrapping<br/>an object?"}
25-
S1 -->|Yes| S2{"Need to change<br/>object's interface?"}
25+
S1 -->|Yes| S2{"Do you need to change<br/>an object's interface?"}
2626
S2 -->|Yes| Adapter["Adapter<br/>(Convert interface)"]
27-
S2 -->|No| S3{"Adding functionality<br/>dynamically?"}
27+
S2 -->|No| S3{"Do you need to add<br/>functionality dynamically?"}
2828
S3 -->|Yes| Decorator["Decorator<br/>(Add behavior)"]
2929
S3 -->|No| Proxy["Proxy<br/>(Control access)"]
3030

31-
S1 -->|No| S4{"Building a tree<br/>structure?"}
31+
S1 -->|No| S4{"Are you building a tree<br/>or hierarchical structure?"}
3232
S4 -->|Yes| Composite["Composite<br/>(Part-whole)"]
33-
S4 -->|No| S5{"Simplifying<br/>complex system?"}
33+
S4 -->|No| S5{"Do you need to simplify<br/>a complex system?"}
3434
S5 -->|Yes| Facade["Facade<br/>(Unified interface)"]
35-
S5 -->|No| S6{"Optimizing<br/>memory?"}
35+
S5 -->|No| S6{"Do you need to optimize<br/>memory usage?"}
3636
S6 -->|Yes| Flyweight["Flyweight<br/>(Share objects)"]
3737
S6 -->|No| Bridge["Bridge<br/>(Decouple interface)"]
3838

3939
%% Behavioral Branch
4040
Behavioral --> B1{"Is it about<br/>sequencing?"}
41-
B1 -->|Yes| B2{"Pass requests<br/>through chain?"}
41+
B1 -->|Yes| B2{"Do you need to pass requests<br/>through a chain of handlers?"}
4242
B2 -->|Yes| ChainOfResp["Chain of Responsibility<br/>(Pass along chain)"]
43-
B2 -->|No| B3{"Execute commands<br/>later?"}
43+
B2 -->|No| B3{"Do you need to execute<br/>commands later or queue them?"}
4444
B3 -->|Yes| Command["Command<br/>(Encapsulate action)"]
4545
B3 -->|No| Iterator["Iterator<br/>(Access elements)"]
4646

47-
B1 -->|No| B4{"Multiple states<br/>or algorithms?"}
48-
B4 -->|Yes| B5{"Object changes<br/>behavior?"}
47+
B1 -->|No| B4{"Does the behavior need<br/>to change dynamically?"}
48+
B4 -->|Yes| B5{"Does the object need to change<br/>behavior based on internal state?"}
4949
B5 -->|Yes| State["State<br/>(Change behavior)"]
5050
B5 -->|No| Strategy["Strategy<br/>(Select algorithm)"]
5151

52-
B4 -->|No| B6{"Need to track<br/>changes?"}
52+
B4 -->|No| B6{"Do you need to track<br/>and notify about changes?"}
5353
B6 -->|Yes| Observer["Observer<br/>(Notify dependents)"]
54-
B6 -->|No| B7{"Saving/restoring<br/>state?"}
54+
B6 -->|No| B7{"Do you need to save<br/>and restore object state?"}
5555
B7 -->|Yes| Memento["Memento<br/>(Capture state)"]
5656
B7 -->|No| Visitor["Visitor<br/>(Add operations)"]
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import React from "react";
2+
import QuestionCard, { YesOrNo } from "./QuestionCard";
3+
import styles from "./navigator.module.css";
4+
5+
interface AnsweredQuestionProps {
6+
question: string;
7+
answer: YesOrNo;
8+
onChangeAnswer: (newAnswer: YesOrNo) => void;
9+
showDivider: boolean;
10+
}
11+
12+
const AnsweredQuestion: React.FC<AnsweredQuestionProps> = ({
13+
question,
14+
answer,
15+
onChangeAnswer,
16+
showDivider,
17+
}) => {
18+
return (
19+
<div>
20+
<div className={styles.answeredQuestion}>
21+
<QuestionCard
22+
question={question}
23+
selectedAnswer={answer}
24+
onAnswerYes={() => {
25+
if (answer !== "yes") {
26+
onChangeAnswer("yes");
27+
}
28+
}}
29+
onAnswerNo={() => {
30+
if (answer !== "no") {
31+
onChangeAnswer("no");
32+
}
33+
}}
34+
isAnswered={true}
35+
/>
36+
</div>
37+
{showDivider && <div className={styles.divider} />}
38+
</div>
39+
);
40+
};
41+
42+
export default AnsweredQuestion;
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import React, { useState } from "react";
2+
import {
3+
decisionTree,
4+
getNode,
5+
getNextNode,
6+
isPatternNode,
7+
} from "@site/src/data/decision-tree";
8+
import QuestionCard from "./QuestionCard";
9+
import PatternResult from "./PatternResult";
10+
import AnsweredQuestion from "./AnsweredQuestion";
11+
import styles from "./navigator.module.css";
12+
13+
export interface Answer {
14+
nodeId: string;
15+
answer: "yes" | "no";
16+
}
17+
18+
const NavigatorContainer: React.FC = () => {
19+
const [answers, setAnswers] = useState<Answer[]>([]);
20+
const currentNodeId =
21+
answers.length === 0
22+
? "q1"
23+
: getNextNode(
24+
answers[answers.length - 1].nodeId,
25+
answers[answers.length - 1].answer
26+
) || "q1";
27+
const currentNode = getNode(currentNodeId);
28+
29+
if (!currentNode) {
30+
return <div>Error: Node not found</div>;
31+
}
32+
33+
const handleAnswer = (answer: "yes" | "no") => {
34+
setAnswers([...answers, { nodeId: currentNodeId, answer }]);
35+
};
36+
37+
const handleGoBack = (index: number, newAnswer?: "yes" | "no") => {
38+
const targetAnswer = newAnswer || answers[index].answer;
39+
setAnswers(
40+
answers
41+
.slice(0, index)
42+
.concat({ nodeId: answers[index].nodeId, answer: targetAnswer })
43+
);
44+
};
45+
46+
const handleReset = () => {
47+
setAnswers([]);
48+
};
49+
50+
const isPattern = isPatternNode(currentNodeId);
51+
52+
return (
53+
<div className={styles.navigatorPage}>
54+
{/* Show all previous questions with their selected answers */}
55+
{answers.map((answer, index) => {
56+
const node = getNode(answer.nodeId);
57+
if (!node) return null;
58+
59+
// Check if the next node after this answer is a pattern (not a question)
60+
const nextNodeId = getNextNode(answer.nodeId, answer.answer);
61+
const isNextNodePattern = nextNodeId
62+
? isPatternNode(nextNodeId)
63+
: false;
64+
65+
return (
66+
<AnsweredQuestion
67+
key={index}
68+
question={node.question || ""}
69+
answer={answer.answer}
70+
onChangeAnswer={(newAnswer) => handleGoBack(index, newAnswer)}
71+
showDivider={!isNextNodePattern}
72+
/>
73+
);
74+
})}
75+
76+
{/* Current Question or Result */}
77+
{isPattern && currentNode.pattern ? (
78+
<PatternResult
79+
name={currentNode.pattern.name}
80+
description={currentNode.pattern.description}
81+
path={currentNode.pattern.path}
82+
onReset={handleReset}
83+
/>
84+
) : (
85+
<QuestionCard
86+
question={currentNode.question || ""}
87+
onAnswerYes={() => handleAnswer("yes")}
88+
onAnswerNo={() => handleAnswer("no")}
89+
/>
90+
)}
91+
</div>
92+
);
93+
};
94+
95+
export default NavigatorContainer;
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import React from "react";
2+
import styles from "./navigator.module.css";
3+
4+
const baseURL = "/DesignPatternPedia/docs";
5+
6+
interface PatternResultProps {
7+
name: string;
8+
description: string;
9+
path: string;
10+
onReset: () => void;
11+
}
12+
13+
const PatternResult: React.FC<PatternResultProps> = ({
14+
name,
15+
description,
16+
path,
17+
onReset,
18+
}) => {
19+
return (
20+
<>
21+
<div className={styles.divider} />
22+
<div className={styles.patternResult}>
23+
<h2>{name}</h2>
24+
<p>{description}</p>
25+
<div className={styles.resultButtons}>
26+
<a href={`${baseURL}/${path}`} className="button button--primary">
27+
Learn More
28+
</a>
29+
<button
30+
onClick={onReset}
31+
className={`button button--outline button--secondary ${styles.resetButton}`}
32+
>
33+
Start Over
34+
</button>
35+
</div>
36+
</div>
37+
</>
38+
);
39+
};
40+
41+
export default PatternResult;
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import React from "react";
2+
import styles from "./navigator.module.css";
3+
4+
export type YesOrNo = "yes" | "no";
5+
6+
interface QuestionCardProps {
7+
question: string;
8+
selectedAnswer?: YesOrNo;
9+
onAnswerYes: () => void;
10+
onAnswerNo: () => void;
11+
isAnswered?: boolean;
12+
}
13+
14+
const QuestionCard: React.FC<QuestionCardProps> = ({
15+
question,
16+
selectedAnswer,
17+
onAnswerYes,
18+
onAnswerNo,
19+
isAnswered = false,
20+
}) => {
21+
return (
22+
<div className={styles.questionContent}>
23+
<h2>{question}</h2>
24+
<div className={styles.answerButtons}>
25+
<button
26+
onClick={onAnswerYes}
27+
className={`${styles.answerBtn} ${styles.answerBtnYes} ${
28+
selectedAnswer === "yes" ? styles.selected : ""
29+
}`}
30+
disabled={isAnswered && selectedAnswer === "yes"}
31+
>
32+
<span className={styles.answerIcon}></span>
33+
<span>Yes</span>
34+
</button>
35+
<button
36+
onClick={onAnswerNo}
37+
className={`${styles.answerBtn} ${styles.answerBtnNo} ${
38+
selectedAnswer === "no" ? styles.selected : ""
39+
}`}
40+
disabled={isAnswered && selectedAnswer === "no"}
41+
>
42+
<span className={styles.answerIcon}></span>
43+
<span>No</span>
44+
</button>
45+
</div>
46+
</div>
47+
);
48+
};
49+
50+
export default QuestionCard;

src/components/navigator/index.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import React from "react";
2+
import NavigatorContainer from "./NavigatorContainer";
3+
4+
const Navigator: React.FC = () => {
5+
return <NavigatorContainer />;
6+
};
7+
8+
export default Navigator;

0 commit comments

Comments
 (0)