@tomaszjarosz/react-visualizers 0.2.13 → 0.2.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +209 -40
- package/dist/index.cjs +166 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +166 -13
- package/dist/index.js.map +1 -1
- package/dist/react-visualizers.css +40 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
# @tomaszjarosz/react-visualizers
|
|
2
2
|
|
|
3
|
-
Interactive algorithm and data structure visualizers for React.
|
|
3
|
+
Interactive algorithm and data structure visualizers for React. Perfect for learning, teaching, and interview preparation.
|
|
4
4
|
|
|
5
|
-
[](https://6934a2d9e17d1e509a92c935-rdicbxowdr.chromatic.com/)
|
|
6
5
|
[](https://www.npmjs.com/package/@tomaszjarosz/react-visualizers)
|
|
6
|
+
[](https://6934a2d9e17d1e509a92c935-rdicbxowdr.chromatic.com/)
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **36 interactive visualizers** covering algorithms, data structures, and distributed systems
|
|
12
|
+
- **Interview Mode** with built-in questions, scoring, hints, and explanations
|
|
13
|
+
- **Step-by-step animations** with playback controls (play, pause, step forward/back)
|
|
14
|
+
- **Code highlighting** showing the current line being executed
|
|
15
|
+
- **Keyboard shortcuts** for efficient navigation
|
|
16
|
+
- **URL state persistence** for sharing specific states
|
|
17
|
+
- **Fully typed** with TypeScript
|
|
18
|
+
- **Zero runtime dependencies** (only `lucide-react` for icons)
|
|
7
19
|
|
|
8
20
|
## Live Demo
|
|
9
21
|
|
|
@@ -13,78 +25,157 @@ Interactive algorithm and data structure visualizers for React.
|
|
|
13
25
|
|
|
14
26
|
```bash
|
|
15
27
|
npm install @tomaszjarosz/react-visualizers
|
|
28
|
+
# or
|
|
29
|
+
pnpm add @tomaszjarosz/react-visualizers
|
|
30
|
+
# or
|
|
31
|
+
yarn add @tomaszjarosz/react-visualizers
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Important:** Import the CSS file in your application entry point:
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
import '@tomaszjarosz/react-visualizers/styles.css';
|
|
16
38
|
```
|
|
17
39
|
|
|
18
40
|
## Requirements
|
|
19
41
|
|
|
20
42
|
- React 17+
|
|
21
|
-
- Tailwind CSS
|
|
43
|
+
- The library includes compiled Tailwind CSS, no additional setup needed
|
|
44
|
+
|
|
45
|
+
## Quick Start
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
import { HashMapVisualizer } from '@tomaszjarosz/react-visualizers';
|
|
49
|
+
import '@tomaszjarosz/react-visualizers/styles.css';
|
|
50
|
+
|
|
51
|
+
function App() {
|
|
52
|
+
return <HashMapVisualizer showControls showCode />;
|
|
53
|
+
}
|
|
54
|
+
```
|
|
22
55
|
|
|
23
56
|
## Available Visualizers
|
|
24
57
|
|
|
25
|
-
### Algorithms
|
|
58
|
+
### Algorithms (6)
|
|
59
|
+
|
|
26
60
|
| Visualizer | Description |
|
|
27
61
|
|------------|-------------|
|
|
28
62
|
| `BinarySearchVisualizer` | Binary search with O(log n) visualization |
|
|
29
|
-
| `SortingVisualizer` |
|
|
30
|
-
| `SortingComparisonVisualizer` | Side-by-side algorithm
|
|
31
|
-
| `GraphVisualizer` | DFS
|
|
32
|
-
| `DijkstraVisualizer` | Shortest path
|
|
33
|
-
| `DPVisualizer` | Dynamic programming table |
|
|
63
|
+
| `SortingVisualizer` | Bubble, Selection, Insertion, Quick, Merge sort |
|
|
64
|
+
| `SortingComparisonVisualizer` | Side-by-side algorithm race |
|
|
65
|
+
| `GraphVisualizer` | DFS and BFS traversal |
|
|
66
|
+
| `DijkstraVisualizer` | Shortest path with distance relaxation |
|
|
67
|
+
| `DPVisualizer` | Dynamic programming table (Fibonacci) |
|
|
68
|
+
|
|
69
|
+
### Data Structures (10)
|
|
34
70
|
|
|
35
|
-
### Data Structures
|
|
36
71
|
| Visualizer | Description |
|
|
37
72
|
|------------|-------------|
|
|
38
|
-
| `ArrayListVisualizer` | Dynamic array with resizing |
|
|
39
|
-
| `LinkedListVisualizer` |
|
|
40
|
-
| `HashMapVisualizer` | Hash table with
|
|
41
|
-
| `HashTableVisualizer` | Hash function internals |
|
|
42
|
-
| `ArrayDequeVisualizer` | Circular buffer
|
|
43
|
-
| `PriorityQueueVisualizer` |
|
|
44
|
-
| `TreeSetVisualizer` | Red-Black tree (BST) |
|
|
45
|
-
| `LinkedHashMapVisualizer` |
|
|
46
|
-
| `EnumSetVisualizer` | Bit vector
|
|
47
|
-
|
|
48
|
-
|
|
73
|
+
| `ArrayListVisualizer` | Dynamic array with amortized resizing |
|
|
74
|
+
| `LinkedListVisualizer` | Singly linked list operations |
|
|
75
|
+
| `HashMapVisualizer` | Hash table with separate chaining |
|
|
76
|
+
| `HashTableVisualizer` | Hash function internals and collisions |
|
|
77
|
+
| `ArrayDequeVisualizer` | Circular buffer double-ended queue |
|
|
78
|
+
| `PriorityQueueVisualizer` | Binary min-heap operations |
|
|
79
|
+
| `TreeSetVisualizer` | Red-Black tree (balanced BST) |
|
|
80
|
+
| `LinkedHashMapVisualizer` | HashMap + insertion order linked list |
|
|
81
|
+
| `EnumSetVisualizer` | Bit vector implementation |
|
|
82
|
+
| `ListComparisonVisualizer` | ArrayList vs LinkedList performance |
|
|
83
|
+
|
|
84
|
+
### Concurrency (4)
|
|
85
|
+
|
|
49
86
|
| Visualizer | Description |
|
|
50
87
|
|------------|-------------|
|
|
51
|
-
| `BlockingQueueVisualizer` | Producer-consumer
|
|
52
|
-
| `ConcurrentHashMapVisualizer` | Segment-based
|
|
53
|
-
| `CopyOnWriteVisualizer` | Copy-on-write pattern |
|
|
88
|
+
| `BlockingQueueVisualizer` | Producer-consumer with blocking |
|
|
89
|
+
| `ConcurrentHashMapVisualizer` | Segment-based concurrent access |
|
|
90
|
+
| `CopyOnWriteVisualizer` | Copy-on-write pattern for reads |
|
|
54
91
|
| `ImmutableCollectionsVisualizer` | Java 9+ immutable collections |
|
|
55
92
|
|
|
56
|
-
###
|
|
93
|
+
### Advanced Data Structures (2)
|
|
94
|
+
|
|
95
|
+
| Visualizer | Description |
|
|
96
|
+
|------------|-------------|
|
|
97
|
+
| `BloomFilterVisualizer` | Probabilistic set membership |
|
|
98
|
+
| `BTreeVisualizer` | B-Tree with node splitting |
|
|
99
|
+
|
|
100
|
+
### Distributed Systems (2)
|
|
101
|
+
|
|
57
102
|
| Visualizer | Description |
|
|
58
103
|
|------------|-------------|
|
|
59
|
-
| `
|
|
60
|
-
| `
|
|
104
|
+
| `ConsistentHashingVisualizer` | Hash ring with virtual nodes |
|
|
105
|
+
| `RaftVisualizer` | Raft consensus algorithm |
|
|
61
106
|
|
|
62
|
-
|
|
107
|
+
### Other (2)
|
|
108
|
+
|
|
109
|
+
| Visualizer | Description |
|
|
110
|
+
|------------|-------------|
|
|
111
|
+
| `GCVisualizer` | JVM generational garbage collection |
|
|
112
|
+
| `SQLJoinVisualizer` | SQL JOIN operations (INNER, LEFT, RIGHT, FULL) |
|
|
113
|
+
|
|
114
|
+
### Interview Mode (10)
|
|
115
|
+
|
|
116
|
+
Interview visualizers include built-in questions with multiple choice answers, hints, and detailed explanations. Perfect for interview preparation.
|
|
117
|
+
|
|
118
|
+
| Visualizer | Topics |
|
|
119
|
+
|------------|--------|
|
|
120
|
+
| `HashMapInterviewVisualizer` | Hashing, collisions, load factor, rehashing |
|
|
121
|
+
| `TreeSetInterviewVisualizer` | BST properties, Red-Black balancing, rotations |
|
|
122
|
+
| `SortingInterviewVisualizer` | Time complexity, stability, space complexity |
|
|
123
|
+
| `GraphInterviewVisualizer` | DFS vs BFS, cycle detection, topological sort |
|
|
124
|
+
| `DijkstraInterviewVisualizer` | Shortest path, negative weights, relaxation |
|
|
125
|
+
| `DPInterviewVisualizer` | Overlapping subproblems, memoization |
|
|
126
|
+
| `BloomFilterInterviewVisualizer` | False positives, hash functions, bit arrays |
|
|
127
|
+
| `BTreeInterviewVisualizer` | Node splitting, disk-based storage, order |
|
|
128
|
+
| `ConsistentHashingInterviewVisualizer` | Virtual nodes, rebalancing, hot spots |
|
|
129
|
+
| `RaftInterviewVisualizer` | Leader election, log replication, safety |
|
|
130
|
+
|
|
131
|
+
## Interview Mode Usage
|
|
63
132
|
|
|
64
133
|
```tsx
|
|
65
|
-
import {
|
|
134
|
+
import { HashMapInterviewVisualizer } from '@tomaszjarosz/react-visualizers';
|
|
66
135
|
|
|
67
|
-
function
|
|
136
|
+
function InterviewPrep() {
|
|
68
137
|
return (
|
|
69
|
-
<
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
138
|
+
<HashMapInterviewVisualizer
|
|
139
|
+
showControls
|
|
140
|
+
showCode
|
|
141
|
+
onComplete={(session) => {
|
|
142
|
+
console.log(`Score: ${session.results.filter(r => r.isCorrect).length}/${session.results.length}`);
|
|
143
|
+
}}
|
|
144
|
+
/>
|
|
73
145
|
);
|
|
74
146
|
}
|
|
75
147
|
```
|
|
76
148
|
|
|
77
|
-
|
|
149
|
+
## Props
|
|
78
150
|
|
|
79
|
-
All visualizers accept these props:
|
|
151
|
+
All visualizers accept these common props:
|
|
80
152
|
|
|
81
153
|
| Prop | Type | Default | Description |
|
|
82
154
|
|------|------|---------|-------------|
|
|
83
155
|
| `showControls` | `boolean` | `true` | Show playback controls |
|
|
84
|
-
| `showCode` | `boolean` | `true` | Show code panel |
|
|
156
|
+
| `showCode` | `boolean` | `true` | Show code panel with highlighting |
|
|
85
157
|
| `className` | `string` | `''` | Additional CSS classes |
|
|
86
158
|
|
|
87
|
-
|
|
159
|
+
Interview visualizers also accept:
|
|
160
|
+
|
|
161
|
+
| Prop | Type | Default | Description |
|
|
162
|
+
|------|------|---------|-------------|
|
|
163
|
+
| `shuffleQuestions` | `boolean` | `false` | Randomize question order |
|
|
164
|
+
| `onComplete` | `(session) => void` | - | Callback when all questions answered |
|
|
165
|
+
|
|
166
|
+
## Keyboard Shortcuts
|
|
167
|
+
|
|
168
|
+
| Key | Action |
|
|
169
|
+
|-----|--------|
|
|
170
|
+
| `Space` | Play/Pause |
|
|
171
|
+
| `→` | Next step |
|
|
172
|
+
| `←` | Previous step |
|
|
173
|
+
| `R` | Reset |
|
|
174
|
+
| `?` | Show help |
|
|
175
|
+
|
|
176
|
+
## Building Custom Visualizers
|
|
177
|
+
|
|
178
|
+
The library exports shared components and hooks for building your own visualizers:
|
|
88
179
|
|
|
89
180
|
```tsx
|
|
90
181
|
import {
|
|
@@ -93,13 +184,83 @@ import {
|
|
|
93
184
|
Legend,
|
|
94
185
|
StatusPanel,
|
|
95
186
|
VisualizationArea,
|
|
96
|
-
|
|
187
|
+
useVisualizerPlayback,
|
|
188
|
+
useInterviewMode,
|
|
189
|
+
} from '@tomaszjarosz/react-visualizers';
|
|
190
|
+
|
|
191
|
+
function CustomVisualizer() {
|
|
192
|
+
const { currentStep, isPlaying, play, pause, next, prev, reset } = useVisualizerPlayback({
|
|
193
|
+
totalSteps: 10,
|
|
194
|
+
intervalMs: 500,
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
return (
|
|
198
|
+
<div>
|
|
199
|
+
<VisualizationArea>
|
|
200
|
+
{/* Your visualization */}
|
|
201
|
+
</VisualizationArea>
|
|
202
|
+
<ControlPanel
|
|
203
|
+
isPlaying={isPlaying}
|
|
204
|
+
onPlay={play}
|
|
205
|
+
onPause={pause}
|
|
206
|
+
onNext={next}
|
|
207
|
+
onPrev={prev}
|
|
208
|
+
onReset={reset}
|
|
209
|
+
currentStep={currentStep}
|
|
210
|
+
totalSteps={10}
|
|
211
|
+
/>
|
|
212
|
+
</div>
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Available Hooks
|
|
218
|
+
|
|
219
|
+
| Hook | Description |
|
|
220
|
+
|------|-------------|
|
|
221
|
+
| `useVisualizerPlayback` | Step-through animation control with play/pause |
|
|
222
|
+
| `useUrlState` | Persist visualizer state in URL for sharing |
|
|
223
|
+
| `useInterviewMode` | Interview session with questions and scoring |
|
|
224
|
+
|
|
225
|
+
### Available Components
|
|
226
|
+
|
|
227
|
+
| Component | Description |
|
|
228
|
+
|-----------|-------------|
|
|
229
|
+
| `ControlPanel` | Play, pause, step, reset buttons |
|
|
230
|
+
| `CodePanel` | Syntax-highlighted code with line highlighting |
|
|
231
|
+
| `Legend` | Color legend for visualization |
|
|
232
|
+
| `StatusPanel` | Current step description |
|
|
233
|
+
| `VisualizationArea` | Container with consistent styling |
|
|
234
|
+
| `HelpPanel` | Keyboard shortcuts overlay |
|
|
235
|
+
| `ArrayInput` | Custom array input for sorting visualizers |
|
|
236
|
+
| `StepHistory` | List of executed steps |
|
|
237
|
+
| `InterviewModePanel` | Question display with options and scoring |
|
|
238
|
+
|
|
239
|
+
## TypeScript
|
|
240
|
+
|
|
241
|
+
The library is fully typed. All types are exported:
|
|
242
|
+
|
|
243
|
+
```tsx
|
|
244
|
+
import type {
|
|
245
|
+
ControlPanelProps,
|
|
246
|
+
LegendItem,
|
|
247
|
+
InterviewQuestion,
|
|
248
|
+
InterviewSession,
|
|
249
|
+
UseVisualizerPlaybackOptions,
|
|
250
|
+
UseInterviewModeReturn,
|
|
97
251
|
} from '@tomaszjarosz/react-visualizers';
|
|
98
252
|
```
|
|
99
253
|
|
|
100
254
|
## Development
|
|
101
255
|
|
|
102
256
|
```bash
|
|
257
|
+
# Clone the repository
|
|
258
|
+
git clone https://github.com/tomaszjarosz/tomaszjarosz-ui.git
|
|
259
|
+
cd tomaszjarosz-ui/packages/react-visualizers
|
|
260
|
+
|
|
261
|
+
# Install dependencies
|
|
262
|
+
pnpm install
|
|
263
|
+
|
|
103
264
|
# Run Storybook locally
|
|
104
265
|
pnpm run storybook
|
|
105
266
|
|
|
@@ -110,6 +271,14 @@ pnpm run build
|
|
|
110
271
|
pnpm run typecheck
|
|
111
272
|
```
|
|
112
273
|
|
|
274
|
+
## Contributing
|
|
275
|
+
|
|
276
|
+
Contributions are welcome! Please read the contributing guidelines before submitting a PR.
|
|
277
|
+
|
|
113
278
|
## License
|
|
114
279
|
|
|
115
|
-
MIT
|
|
280
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
281
|
+
|
|
282
|
+
## Author
|
|
283
|
+
|
|
284
|
+
**Tomasz Jarosz** - [tomaszjarosz.dev](https://tomaszjarosz.dev)
|
package/dist/index.cjs
CHANGED
|
@@ -1637,11 +1637,46 @@ const ALGORITHM_NAMES$1 = {
|
|
|
1637
1637
|
merge: "MergeSort"
|
|
1638
1638
|
};
|
|
1639
1639
|
const ALGORITHM_COMPLEXITIES$1 = {
|
|
1640
|
-
bubble: {
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1640
|
+
bubble: {
|
|
1641
|
+
time: "O(n²)",
|
|
1642
|
+
best: "O(n)",
|
|
1643
|
+
average: "O(n²)",
|
|
1644
|
+
worst: "O(n²)",
|
|
1645
|
+
space: "O(1)",
|
|
1646
|
+
stable: true
|
|
1647
|
+
},
|
|
1648
|
+
selection: {
|
|
1649
|
+
time: "O(n²)",
|
|
1650
|
+
best: "O(n²)",
|
|
1651
|
+
average: "O(n²)",
|
|
1652
|
+
worst: "O(n²)",
|
|
1653
|
+
space: "O(1)",
|
|
1654
|
+
stable: false
|
|
1655
|
+
},
|
|
1656
|
+
insertion: {
|
|
1657
|
+
time: "O(n²)",
|
|
1658
|
+
best: "O(n)",
|
|
1659
|
+
average: "O(n²)",
|
|
1660
|
+
worst: "O(n²)",
|
|
1661
|
+
space: "O(1)",
|
|
1662
|
+
stable: true
|
|
1663
|
+
},
|
|
1664
|
+
quick: {
|
|
1665
|
+
time: "O(n log n)",
|
|
1666
|
+
best: "O(n log n)",
|
|
1667
|
+
average: "O(n log n)",
|
|
1668
|
+
worst: "O(n²)",
|
|
1669
|
+
space: "O(log n)",
|
|
1670
|
+
stable: false
|
|
1671
|
+
},
|
|
1672
|
+
merge: {
|
|
1673
|
+
time: "O(n log n)",
|
|
1674
|
+
best: "O(n log n)",
|
|
1675
|
+
average: "O(n log n)",
|
|
1676
|
+
worst: "O(n log n)",
|
|
1677
|
+
space: "O(n)",
|
|
1678
|
+
stable: true
|
|
1679
|
+
}
|
|
1645
1680
|
};
|
|
1646
1681
|
const ALGORITHM_CODE$1 = {
|
|
1647
1682
|
bubble: [
|
|
@@ -2642,6 +2677,7 @@ const SortingVisualizerComponent = ({
|
|
|
2642
2677
|
const [customArray, setCustomArray] = React.useState(null);
|
|
2643
2678
|
const [historyCollapsed, setHistoryCollapsed] = React.useState(true);
|
|
2644
2679
|
const [urlStateLoaded, setUrlStateLoaded] = React.useState(false);
|
|
2680
|
+
const [showComplexityTable, setShowComplexityTable] = React.useState(false);
|
|
2645
2681
|
const playingRef = React.useRef(false);
|
|
2646
2682
|
const timeoutRef = React.useRef(null);
|
|
2647
2683
|
const VISUALIZER_ID = "sorting-visualizer";
|
|
@@ -2946,6 +2982,63 @@ const SortingVisualizerComponent = ({
|
|
|
2946
2982
|
/* @__PURE__ */ jsxRuntime.jsx(HelpPanel, {})
|
|
2947
2983
|
] })
|
|
2948
2984
|
] }) }),
|
|
2985
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-4 pb-2", children: [
|
|
2986
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2987
|
+
"button",
|
|
2988
|
+
{
|
|
2989
|
+
onClick: () => setShowComplexityTable(!showComplexityTable),
|
|
2990
|
+
className: "flex items-center gap-2 text-sm text-indigo-600 hover:text-indigo-800 font-medium transition-colors",
|
|
2991
|
+
children: [
|
|
2992
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2993
|
+
"svg",
|
|
2994
|
+
{
|
|
2995
|
+
className: `w-4 h-4 transition-transform ${showComplexityTable ? "rotate-90" : ""}`,
|
|
2996
|
+
fill: "none",
|
|
2997
|
+
viewBox: "0 0 24 24",
|
|
2998
|
+
stroke: "currentColor",
|
|
2999
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5l7 7-7 7" })
|
|
3000
|
+
}
|
|
3001
|
+
),
|
|
3002
|
+
"Algorithm Complexity Comparison"
|
|
3003
|
+
]
|
|
3004
|
+
}
|
|
3005
|
+
),
|
|
3006
|
+
showComplexityTable && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-3 p-4 bg-gradient-to-r from-indigo-50 to-blue-50 rounded-xl border border-indigo-200", children: [
|
|
3007
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "w-full text-sm", children: [
|
|
3008
|
+
/* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { className: "text-left text-indigo-900", children: [
|
|
3009
|
+
/* @__PURE__ */ jsxRuntime.jsx("th", { className: "pb-2 pr-4 font-semibold", children: "Algorithm" }),
|
|
3010
|
+
/* @__PURE__ */ jsxRuntime.jsx("th", { className: "pb-2 px-3 font-semibold text-center", children: "Best" }),
|
|
3011
|
+
/* @__PURE__ */ jsxRuntime.jsx("th", { className: "pb-2 px-3 font-semibold text-center", children: "Average" }),
|
|
3012
|
+
/* @__PURE__ */ jsxRuntime.jsx("th", { className: "pb-2 px-3 font-semibold text-center", children: "Worst" }),
|
|
3013
|
+
/* @__PURE__ */ jsxRuntime.jsx("th", { className: "pb-2 px-3 font-semibold text-center", children: "Space" }),
|
|
3014
|
+
/* @__PURE__ */ jsxRuntime.jsx("th", { className: "pb-2 pl-3 font-semibold text-center", children: "Stable" })
|
|
3015
|
+
] }) }),
|
|
3016
|
+
/* @__PURE__ */ jsxRuntime.jsx("tbody", { className: "text-gray-700", children: Object.keys(ALGORITHM_NAMES$1).map((alg) => {
|
|
3017
|
+
const comp = ALGORITHM_COMPLEXITIES$1[alg];
|
|
3018
|
+
const isCurrentAlgorithm = alg === algorithm;
|
|
3019
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3020
|
+
"tr",
|
|
3021
|
+
{
|
|
3022
|
+
className: `border-t border-indigo-100 ${isCurrentAlgorithm ? "bg-indigo-100/50 font-medium" : ""}`,
|
|
3023
|
+
children: [
|
|
3024
|
+
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "py-2 pr-4", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: isCurrentAlgorithm ? "text-indigo-700" : "", children: ALGORITHM_NAMES$1[alg] }) }),
|
|
3025
|
+
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "py-2 px-3 text-center", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: `px-2 py-0.5 rounded text-xs ${comp.best.includes("log") ? "bg-green-100 text-green-700" : "bg-yellow-100 text-yellow-700"}`, children: comp.best }) }),
|
|
3026
|
+
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "py-2 px-3 text-center", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: `px-2 py-0.5 rounded text-xs ${comp.average.includes("log") ? "bg-green-100 text-green-700" : "bg-yellow-100 text-yellow-700"}`, children: comp.average }) }),
|
|
3027
|
+
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "py-2 px-3 text-center", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: `px-2 py-0.5 rounded text-xs ${comp.worst.includes("log") ? "bg-green-100 text-green-700" : "bg-red-100 text-red-700"}`, children: comp.worst }) }),
|
|
3028
|
+
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "py-2 px-3 text-center", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: `px-2 py-0.5 rounded text-xs ${comp.space === "O(1)" ? "bg-green-100 text-green-700" : "bg-purple-100 text-purple-700"}`, children: comp.space }) }),
|
|
3029
|
+
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "py-2 pl-3 text-center", children: comp.stable ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-green-600", children: "✓" }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-red-400", children: "✗" }) })
|
|
3030
|
+
]
|
|
3031
|
+
},
|
|
3032
|
+
alg
|
|
3033
|
+
);
|
|
3034
|
+
}) })
|
|
3035
|
+
] }) }),
|
|
3036
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "mt-3 text-xs text-indigo-600", children: [
|
|
3037
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: "Stable:" }),
|
|
3038
|
+
" A sorting algorithm is stable if elements with equal keys maintain their relative order."
|
|
3039
|
+
] })
|
|
3040
|
+
] })
|
|
3041
|
+
] }),
|
|
2949
3042
|
showControls && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-4 py-3 bg-gray-50 border-t border-gray-200", children: [
|
|
2950
3043
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2951
3044
|
ControlPanel,
|
|
@@ -17460,15 +17553,75 @@ const SQLJoinVisualizerComponent = ({
|
|
|
17460
17553
|
}) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-[10px] text-gray-400 text-center py-2", children: "No results yet" })
|
|
17461
17554
|
] })
|
|
17462
17555
|
] }),
|
|
17463
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-4 p-3 bg-blue-50 rounded-lg", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "
|
|
17464
|
-
/* @__PURE__ */ jsxRuntime.jsxs("
|
|
17465
|
-
|
|
17466
|
-
|
|
17556
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-4 p-3 bg-gradient-to-r from-cyan-50 to-blue-50 rounded-lg border border-cyan-200", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-4", children: [
|
|
17557
|
+
/* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 100 60", className: "w-24 h-14 flex-shrink-0", children: [
|
|
17558
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
17559
|
+
"circle",
|
|
17560
|
+
{
|
|
17561
|
+
cx: "35",
|
|
17562
|
+
cy: "30",
|
|
17563
|
+
r: "22",
|
|
17564
|
+
fill: joinType === "left" || joinType === "full" ? "#06b6d4" : joinType === "inner" ? "transparent" : "#e5e7eb",
|
|
17565
|
+
fillOpacity: joinType === "left" || joinType === "full" ? 0.6 : joinType === "inner" ? 0 : 0.5,
|
|
17566
|
+
stroke: "#0891b2",
|
|
17567
|
+
strokeWidth: "2"
|
|
17568
|
+
}
|
|
17569
|
+
),
|
|
17570
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
17571
|
+
"circle",
|
|
17572
|
+
{
|
|
17573
|
+
cx: "65",
|
|
17574
|
+
cy: "30",
|
|
17575
|
+
r: "22",
|
|
17576
|
+
fill: joinType === "right" || joinType === "full" ? "#06b6d4" : joinType === "inner" ? "transparent" : "#e5e7eb",
|
|
17577
|
+
fillOpacity: joinType === "right" || joinType === "full" ? 0.6 : joinType === "inner" ? 0 : 0.5,
|
|
17578
|
+
stroke: "#0891b2",
|
|
17579
|
+
strokeWidth: "2"
|
|
17580
|
+
}
|
|
17581
|
+
),
|
|
17582
|
+
/* @__PURE__ */ jsxRuntime.jsx("clipPath", { id: "leftClip", children: /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "35", cy: "30", r: "22" }) }),
|
|
17583
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
17584
|
+
"circle",
|
|
17585
|
+
{
|
|
17586
|
+
cx: "65",
|
|
17587
|
+
cy: "30",
|
|
17588
|
+
r: "22",
|
|
17589
|
+
fill: "#22c55e",
|
|
17590
|
+
fillOpacity: "0.7",
|
|
17591
|
+
clipPath: "url(#leftClip)"
|
|
17592
|
+
}
|
|
17593
|
+
),
|
|
17594
|
+
/* @__PURE__ */ jsxRuntime.jsx("text", { x: "22", y: "33", fontSize: "8", fill: "#0e7490", fontWeight: "bold", children: "L" }),
|
|
17595
|
+
/* @__PURE__ */ jsxRuntime.jsx("text", { x: "74", y: "33", fontSize: "8", fill: "#0e7490", fontWeight: "bold", children: "R" })
|
|
17467
17596
|
] }),
|
|
17468
|
-
|
|
17469
|
-
|
|
17470
|
-
|
|
17471
|
-
|
|
17597
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1", children: [
|
|
17598
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-sm font-semibold text-cyan-800 mb-1", children: [
|
|
17599
|
+
joinType.toUpperCase(),
|
|
17600
|
+
" JOIN"
|
|
17601
|
+
] }),
|
|
17602
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-cyan-700", children: [
|
|
17603
|
+
joinType === "inner" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
17604
|
+
"Returns only rows with matches in ",
|
|
17605
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: "both" }),
|
|
17606
|
+
" tables (green intersection)."
|
|
17607
|
+
] }),
|
|
17608
|
+
joinType === "left" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
17609
|
+
"Returns ",
|
|
17610
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: "all" }),
|
|
17611
|
+
" rows from left table + matching rows from right. NULL if no match."
|
|
17612
|
+
] }),
|
|
17613
|
+
joinType === "right" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
17614
|
+
"Returns ",
|
|
17615
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: "all" }),
|
|
17616
|
+
" rows from right table + matching rows from left. NULL if no match."
|
|
17617
|
+
] }),
|
|
17618
|
+
joinType === "full" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
17619
|
+
"Returns ",
|
|
17620
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: "all" }),
|
|
17621
|
+
" rows from both tables. NULL where no match exists."
|
|
17622
|
+
] })
|
|
17623
|
+
] })
|
|
17624
|
+
] })
|
|
17472
17625
|
] }) }),
|
|
17473
17626
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
17474
17627
|
StatusPanel,
|