|
1 | | -import { useState } from "react"; |
2 | | -import { OverflowList } from "react-responsive-overflow-list"; |
3 | | -import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; |
4 | | -import { tomorrow } from "react-syntax-highlighter/dist/esm/styles/prism"; |
5 | | -import { Theme, Switch } from "@radix-ui/themes"; |
| 1 | +import { Theme } from "@radix-ui/themes"; |
6 | 2 | import { CustomOverflowExample } from "./examples/CustomOverflowExample"; |
7 | | -import { RadixVirtualizedOverflowList } from "./examples/RadixVirtualizedOverflowList"; |
| 3 | +import { BasicExample } from "./examples/BasicExample"; |
| 4 | +import { ChildrenPatternExample } from "./examples/ChildrenPatternExample"; |
| 5 | +import { MultiRowExample } from "./examples/MultiRowExample"; |
| 6 | +import { CustomHostElementExample } from "./examples/CustomHostElementExample"; |
| 7 | +import { RadixVirtualizationExample } from "./examples/RadixVirtualizationExample"; |
| 8 | +import { FlushImmediatelyExample } from "./examples/FlushImmediatelyExample"; |
| 9 | +import { OneItemWiderExample } from "./examples/OneItemWiderExample"; |
| 10 | +import { MaxRowsOverflowExample } from "./examples/MaxRowsOverflowExample"; |
8 | 11 | import { Github } from "lucide-react"; |
9 | 12 | import "./App.css"; |
10 | 13 |
|
11 | | -const fruits = ["Apple", "Banana", "Cherry", "Date", "Elderberry", "Fig", "Grape", "Honeydew", "Kiwi", "Lemon"]; |
12 | | - |
13 | | -const tags = ["React", "TypeScript", "CSS", "HTML", "JavaScript", "Node.js", "Express", "MongoDB", "Vite", "ESLint"]; |
14 | | - |
15 | | -const menuItems = ["Home", "About", "Services", "Portfolio", "Blog", "Contact", "Careers", "Support"]; |
16 | | - |
17 | | -function MultiRowExample() { |
18 | | - const [maxRows, setMaxRows] = useState(2); |
19 | | - |
20 | | - return ( |
21 | | - <section className="demo"> |
22 | | - <h2 id="multi-row-example">Multi-row Example</h2> |
23 | | - <p>Allow up to {maxRows} rows before overflow</p> |
24 | | - <div className="code-preview"> |
25 | | - <SyntaxHighlighter language="tsx" style={tomorrow}> |
26 | | - {`<OverflowList |
27 | | - items={fruits.concat(tags).concat(menuItems)} |
28 | | - renderItem={(item) => <span className="multi-item">{item}</span>} |
29 | | - maxRows={${maxRows}} |
30 | | - style={{ gap: "4px" }} |
31 | | -/>`} |
32 | | - </SyntaxHighlighter> |
33 | | - </div> |
34 | | - <div className="controls"> |
35 | | - <label htmlFor="maxRows">Max Rows:</label> |
36 | | - <input |
37 | | - id="maxRows" |
38 | | - type="number" |
39 | | - min="1" |
40 | | - max="10" |
41 | | - value={maxRows} |
42 | | - onChange={(e) => setMaxRows(parseInt(e.target.value) || 1)} |
43 | | - className="max-rows-input" |
44 | | - /> |
45 | | - </div> |
46 | | - |
47 | | - <div className="demo-container"> |
48 | | - <OverflowList |
49 | | - items={fruits.concat(tags).concat(menuItems)} |
50 | | - renderItem={(item) => <span className="multi-item">{item}</span>} |
51 | | - maxRows={maxRows} |
52 | | - style={{ gap: "4px" }} |
53 | | - /> |
54 | | - </div> |
55 | | - </section> |
56 | | - ); |
57 | | -} |
58 | | - |
59 | | -function FlushImmediatelyExample() { |
60 | | - const [flushImmediately, setFlushImmediately] = useState(false); |
61 | | - |
62 | | - return ( |
63 | | - <section className="demo"> |
64 | | - <h2 id="flush-immediately-example">Flush Immediately Example</h2> |
65 | | - <p> |
66 | | - Control how updates are applied when the container resizes. |
67 | | - <strong>flushImmediately={flushImmediately ? "true" : "false"}</strong> |
68 | | - (default: true) |
69 | | - </p> |
70 | | - <div className="code-preview"> |
71 | | - <SyntaxHighlighter language="tsx" style={tomorrow}> |
72 | | - {`<OverflowList |
73 | | - items={fruits.concat(tags)} |
74 | | - renderItem={(item) => <span className="multi-item">{item}</span>} |
75 | | - flushImmediately={${flushImmediately}} |
76 | | - style={{ gap: "4px" }} |
77 | | -/>`} |
78 | | - </SyntaxHighlighter> |
79 | | - </div> |
80 | | - <div className="controls"> |
81 | | - <div style={{ display: "flex", alignItems: "center", gap: "8px" }}> |
82 | | - <label htmlFor="flush-toggle">Flush Immediately:</label> |
83 | | - <Switch id="flush-toggle" checked={flushImmediately} onCheckedChange={setFlushImmediately} /> |
84 | | - <span style={{ fontSize: "14px", color: "#666" }}> |
85 | | - {flushImmediately ? "Enabled (No flickering)" : "Disabled (better performance)"} |
86 | | - </span> |
87 | | - </div> |
88 | | - </div> |
89 | | - |
90 | | - <div style={{ marginBottom: "16px", padding: "12px", backgroundColor: "#f5f5f5", borderRadius: "4px" }}> |
91 | | - <h4 style={{ margin: "0 0 8px 0", fontSize: "14px" }}>Trade-offs:</h4> |
92 | | - <ul style={{ margin: 0, paddingLeft: "20px", fontSize: "14px" }}> |
93 | | - <li> |
94 | | - <strong>flushImmediately=true:</strong> Updates are applied immediately using flushSync, avoiding flickering |
95 | | - but may impact performance |
96 | | - </li> |
97 | | - <li> |
98 | | - <strong>flushImmediately=false:</strong> Updates are applied in the requestAnimationFrame callback, avoiding |
99 | | - forced reflow and improving performance but may cause slight flickering |
100 | | - </li> |
101 | | - <li> |
102 | | - <strong>Default behavior:</strong> flushImmediately is true by default to prioritize smooth visual |
103 | | - experience |
104 | | - </li> |
105 | | - </ul> |
106 | | - |
107 | | - <div style={{ marginTop: "12px", fontStyle: "italic", color: "#888", fontSize: "14px" }}> |
108 | | - Resize quickly below to observe the difference! |
109 | | - </div> |
110 | | - </div> |
111 | | - |
112 | | - <div className="demo-container"> |
113 | | - <OverflowList |
114 | | - items={fruits} |
115 | | - renderItem={(item) => <span className="multi-item">{item}</span>} |
116 | | - flushImmediately={flushImmediately} |
117 | | - style={{ gap: "4px" }} |
118 | | - /> |
119 | | - </div> |
120 | | - </section> |
121 | | - ); |
122 | | -} |
123 | | - |
124 | 14 | function App() { |
125 | 15 | return ( |
126 | 16 | <Theme> |
@@ -150,188 +40,15 @@ function App() { |
150 | 40 | </header> |
151 | 41 |
|
152 | 42 | <main> |
153 | | - <section className="demo"> |
154 | | - <h2 id="basic-example">Basic Example</h2> |
155 | | - <p>Simple list with default overflow element</p> |
156 | | - <div className="code-preview"> |
157 | | - <SyntaxHighlighter language="tsx" style={tomorrow}> |
158 | | - {`<OverflowList |
159 | | - items={fruits} |
160 | | - renderItem={(item, index) => ( |
161 | | - <span key={index} className="fruit-item"> |
162 | | - {item} |
163 | | - </span> |
164 | | - )} |
165 | | - style={{ gap: "8px" }} |
166 | | -/>`} |
167 | | - </SyntaxHighlighter> |
168 | | - </div> |
169 | | - <div className="demo-container"> |
170 | | - <OverflowList |
171 | | - items={fruits} |
172 | | - renderItem={(item, index) => ( |
173 | | - <span key={index} className="fruit-item"> |
174 | | - {item} |
175 | | - </span> |
176 | | - )} |
177 | | - style={{ gap: "8px" }} |
178 | | - /> |
179 | | - </div> |
180 | | - </section> |
181 | | - |
182 | | - <section className="demo"> |
183 | | - <h2 id="children-pattern">Children Pattern</h2> |
184 | | - <p>Using children instead of items array</p> |
185 | | - <div className="code-preview"> |
186 | | - <SyntaxHighlighter language="tsx" style={tomorrow}> |
187 | | - {`<OverflowList> |
188 | | - <button>Action 1</button> |
189 | | - <button>Action 2</button> |
190 | | - ... |
191 | | -</OverflowList>`} |
192 | | - </SyntaxHighlighter> |
193 | | - </div> |
194 | | - <div className="demo-container"> |
195 | | - <OverflowList style={{ gap: "8px" }}> |
196 | | - <button className="action-button">Action 1</button> |
197 | | - <button className="action-button">Action 2</button> |
198 | | - <button className="action-button">Action 3</button> |
199 | | - <button className="action-button">Action 4</button> |
200 | | - <button className="action-button">Action 5</button> |
201 | | - <button className="action-button">Action 6</button> |
202 | | - </OverflowList> |
203 | | - </div> |
204 | | - </section> |
205 | | - |
| 43 | + <BasicExample /> |
| 44 | + <ChildrenPatternExample /> |
206 | 45 | <MultiRowExample /> |
207 | | - |
208 | 46 | <CustomOverflowExample /> |
209 | | - |
210 | | - <section className="demo"> |
211 | | - <h2 id="custom-host-element">Custom Host Element</h2> |
212 | | - <p>Using the 'as' prop to render as different HTML elements</p> |
213 | | - <div className="code-preview"> |
214 | | - <SyntaxHighlighter language="tsx" style={tomorrow}> |
215 | | - {`<OverflowList as="nav" style={{ gap: "8px" }}> |
216 | | - <a href="#home">Home</a> |
217 | | - <a href="#about">About</a> |
218 | | - <a href="#contact">Contact</a> |
219 | | -</OverflowList>`} |
220 | | - </SyntaxHighlighter> |
221 | | - </div> |
222 | | - <div className="demo-container"> |
223 | | - <OverflowList as="nav" style={{ gap: "8px" }}> |
224 | | - <a href="#home" className="demo-item demo-item--primary"> |
225 | | - Home |
226 | | - </a> |
227 | | - <a href="#about" className="demo-item demo-item--primary"> |
228 | | - About |
229 | | - </a> |
230 | | - <a href="#contact" className="demo-item demo-item--primary"> |
231 | | - Contact |
232 | | - </a> |
233 | | - <a href="#services" className="demo-item demo-item--primary"> |
234 | | - Services |
235 | | - </a> |
236 | | - <a href="#portfolio" className="demo-item demo-item--primary"> |
237 | | - Portfolio |
238 | | - </a> |
239 | | - </OverflowList> |
240 | | - </div> |
241 | | - </section> |
242 | | - |
243 | | - <section className="demo"> |
244 | | - <h2 id="radix-ui-virtualization-example">Radix UI + Virtualization Example</h2> |
245 | | - <p> |
246 | | - This is an EXAMPLE implementation showing how to wrap OverflowList with Radix UI dropdown and |
247 | | - virtualization. In real-world applications, it's expected that you'll wrap OverflowList with your own |
248 | | - components tailored to your specific needs, design system, and UI framework. |
249 | | - </p> |
250 | | - <div className="code-preview"> |
251 | | - <SyntaxHighlighter language="tsx" style={tomorrow}> |
252 | | - {`import { RadixVirtualizedOverflowList } from "./examples/RadixVirtualizedOverflowList"; |
253 | | -
|
254 | | -// Small dataset - uses simple dropdown |
255 | | -<RadixVirtualizedOverflowList |
256 | | - items={tags} |
257 | | - renderItem={(tag) => <span className="tag">#{tag}</span>} |
258 | | - style={{ gap: "6px" }} |
259 | | -/> |
260 | | -
|
261 | | -// Large dataset - automatically uses virtualization |
262 | | -<RadixVirtualizedOverflowList |
263 | | - items={Array.from({ length: 1000 }, (_, i) => \`Item \${i + 1}\`)} |
264 | | - renderItem={(item) => <span className="tag">#{item}</span>} |
265 | | - virtualizationThreshold={50} |
266 | | - enableSearch={true} |
267 | | - style={{ gap: "6px" }} |
268 | | -/>`} |
269 | | - </SyntaxHighlighter> |
270 | | - </div> |
271 | | - |
272 | | - <div className="demo-container"> |
273 | | - <h4 style={{ margin: "0 0 12px 0", fontSize: "16px" }}>Small Dataset (Simple Dropdown)</h4> |
274 | | - <RadixVirtualizedOverflowList |
275 | | - items={tags} |
276 | | - renderItem={(tag) => <span className="tag">#{tag}</span>} |
277 | | - style={{ gap: "6px" }} |
278 | | - /> |
279 | | - </div> |
280 | | - |
281 | | - <div className="demo-container" style={{ marginTop: "24px" }}> |
282 | | - <h4 style={{ margin: "0 0 12px 0", fontSize: "16px" }}> |
283 | | - Large Dataset (Virtualized Dropdown with Search) |
284 | | - </h4> |
285 | | - <RadixVirtualizedOverflowList |
286 | | - items={Array.from({ length: 1000 }, (_, i) => `Item ${i + 1}`)} |
287 | | - renderItem={(item) => <span className="tag">#{item}</span>} |
288 | | - virtualizationThreshold={50} |
289 | | - enableSearch={true} |
290 | | - searchPlaceholder="Search items..." |
291 | | - style={{ gap: "6px" }} |
292 | | - /> |
293 | | - </div> |
294 | | - |
295 | | - <div className="demo-note"> |
296 | | - <strong>This example demonstrates:</strong> |
297 | | - <ul style={{ margin: "8px 0", paddingLeft: "20px" }}> |
298 | | - <li> |
299 | | - <strong>Automatic virtualization:</strong> Switches to virtualized dropdown when item count exceeds |
300 | | - threshold |
301 | | - </li> |
302 | | - <li> |
303 | | - <strong>Search functionality:</strong> Built-in search/filter for large datasets |
304 | | - </li> |
305 | | - <li> |
306 | | - <strong>Radix UI integration:</strong> Full accessibility and keyboard navigation support |
307 | | - </li> |
308 | | - <li> |
309 | | - <strong>Customizable:</strong> Configurable thresholds, styling, and behavior |
310 | | - </li> |
311 | | - <li> |
312 | | - <strong>Performance optimized:</strong> Efficient rendering for thousands of items |
313 | | - </li> |
314 | | - </ul> |
315 | | - <p style={{ margin: "12px 0 0 0", fontStyle: "italic", color: "#666" }}> |
316 | | - <strong>Note:</strong> This is just an example implementation. In real-world applications, it's expected |
317 | | - that you'll wrap OverflowList with your own components tailored to your specific needs and design |
318 | | - system. |
319 | | - <br /> |
320 | | - <strong>Source:</strong>{" "} |
321 | | - <a |
322 | | - href="https://github.com/eliav2/react-responsive-overflow-list/blob/main/demo/src/examples/RadixVirtualizedOverflowList.tsx" |
323 | | - target="_blank" |
324 | | - rel="noopener noreferrer" |
325 | | - > |
326 | | - View implementation on GitHub |
327 | | - </a> |
328 | | - </p> |
329 | | - </div> |
330 | | - </section> |
331 | | - |
| 47 | + <CustomHostElementExample /> |
| 48 | + <RadixVirtualizationExample /> |
332 | 49 | <FlushImmediatelyExample /> |
333 | | - |
334 | | - {/* <div className="demo-container"></div> */} |
| 50 | + <OneItemWiderExample /> |
| 51 | + <MaxRowsOverflowExample /> |
335 | 52 | </main> |
336 | 53 |
|
337 | 54 | <footer> |
|
0 commit comments