@smilodon/core 1.3.6 → 1.3.10

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 CHANGED
@@ -10,6 +10,31 @@
10
10
  </p>
11
11
  </div>
12
12
 
13
+ ## 📖 Documentation
14
+
15
+ **For comprehensive documentation covering all features, styling options, and advanced patterns:**
16
+
17
+ 👉 **[Complete Vanilla JS Guide](../vanilla/COMPLETE-GUIDE.md)** 👈
18
+
19
+ Additional references:
20
+ - **Performance Runbook**: [`docs/PERFORMANCE-RUNBOOK.md`](../../docs/PERFORMANCE-RUNBOOK.md)
21
+ - **Performance Guide**: [`docs/PERFORMANCE.md`](../../docs/PERFORMANCE.md)
22
+ - **Known Limitations**: [`docs/KNOWN-LIMITATIONS.md`](../../docs/KNOWN-LIMITATIONS.md)
23
+
24
+ The complete guide includes:
25
+ - ✅ All 60+ CSS variables for complete customization
26
+ - ✅ Vanilla JavaScript patterns (DOM manipulation, event listeners)
27
+ - ✅ Complete API reference with all properties and methods
28
+ - ✅ CDN usage and module bundler integration
29
+ - ✅ Custom renderers with HTML templates
30
+ - ✅ Theme examples and dynamic styling
31
+ - ✅ Advanced patterns (async loading, local storage, dependent selects)
32
+ - ✅ Troubleshooting and accessibility information
33
+
34
+ > **WebKit e2e note (Linux/Arch)**: WebKit Playwright binaries depend on older system libraries; on Arch-based distros we recommend running WebKit tests via the Playwright Docker image.
35
+
36
+ ---
37
+
13
38
  ## Why Smilodon?
14
39
 
15
40
  Smilodon is a Web Component that renders **1,000,000+ items at 60 FPS** with constant DOM size, sub-millisecond search, and zero framework lock-in. Built for extreme-scale data applications where legacy libraries crash or lag.
@@ -171,6 +196,307 @@ select.items = [1, 2, 3, 5, 8, 13, 21, 34, 55, 89];
171
196
  // ]
172
197
  ```
173
198
 
199
+ ---
200
+
201
+ ## 🎯 Two Ways to Specify Options
202
+
203
+ Smilodon provides **two powerful approaches** for defining select options, each optimized for different use cases:
204
+
205
+ ### Method 1: Data-Driven (Object Arrays) 📊
206
+
207
+ **Use when**: You have structured data and want simple, declarative option rendering.
208
+
209
+ **Advantages**:
210
+ - ✅ Simple and declarative
211
+ - ✅ Auto-conversion from strings/numbers
212
+ - ✅ Perfect for basic dropdowns
213
+ - ✅ Zero boilerplate code
214
+ - ✅ Extremely performant (millions of items)
215
+ - ✅ Built-in search and filtering
216
+ - ✅ TypeScript type safety
217
+
218
+ **Examples**:
219
+
220
+ ```javascript
221
+ // Simple object array
222
+ const select = document.querySelector('enhanced-select');
223
+
224
+ select.items = [
225
+ { value: '1', label: 'Apple' },
226
+ { value: '2', label: 'Banana' },
227
+ { value: '3', label: 'Cherry' }
228
+ ];
229
+
230
+ // With additional metadata
231
+ select.items = [
232
+ { value: 'us', label: 'United States', disabled: false },
233
+ { value: 'ca', label: 'Canada', disabled: false },
234
+ { value: 'mx', label: 'Mexico', disabled: true } // Disabled option
235
+ ];
236
+
237
+ // With grouping
238
+ select.items = [
239
+ { value: 'apple', label: 'Apple', group: 'Fruits' },
240
+ { value: 'banana', label: 'Banana', group: 'Fruits' },
241
+ { value: 'carrot', label: 'Carrot', group: 'Vegetables' },
242
+ { value: 'broccoli', label: 'Broccoli', group: 'Vegetables' }
243
+ ];
244
+
245
+ // Auto-conversion from strings
246
+ select.items = ['Red', 'Green', 'Blue', 'Yellow'];
247
+
248
+ // Auto-conversion from numbers
249
+ select.items = [10, 20, 30, 40, 50];
250
+
251
+ // Large datasets (millions of items)
252
+ select.items = Array.from({ length: 1_000_000 }, (_, i) => ({
253
+ value: i,
254
+ label: `Item ${i + 1}`
255
+ }));
256
+ ```
257
+
258
+ ### Method 2: Component-Driven (Custom Renderers) 🎨
259
+
260
+ **Use when**: You need rich, interactive option content with custom HTML/styling.
261
+
262
+ **Advantages**:
263
+ - ✅ Full control over option rendering
264
+ - ✅ Rich content (images, icons, badges, multi-line text)
265
+ - ✅ Custom HTML and styling
266
+ - ✅ Interactive elements within options
267
+ - ✅ Conditional rendering based on item data
268
+ - ✅ Perfect for complex UIs (user cards, product listings, etc.)
269
+
270
+ **How it works**: Provide an `optionTemplate` function that returns HTML string for each option.
271
+
272
+ **Examples**:
273
+
274
+ ```javascript
275
+ const select = document.querySelector('enhanced-select');
276
+
277
+ // Example 1: Simple custom template with icons
278
+ const items = [
279
+ { value: 'js', label: 'JavaScript', icon: '🟨', description: 'Dynamic scripting language' },
280
+ { value: 'py', label: 'Python', icon: '🐍', description: 'General-purpose programming' },
281
+ { value: 'rs', label: 'Rust', icon: '🦀', description: 'Systems programming language' }
282
+ ];
283
+
284
+ select.items = items;
285
+ select.optionTemplate = (item, index) => `
286
+ <div style="display: flex; align-items: center; gap: 12px;">
287
+ <span style="font-size: 24px;">${item.icon}</span>
288
+ <div>
289
+ <div style="font-weight: 600;">${item.label}</div>
290
+ <div style="font-size: 12px; color: #6b7280;">${item.description}</div>
291
+ </div>
292
+ </div>
293
+ `;
294
+
295
+ // Example 2: User selection with avatars
296
+ const users = [
297
+ {
298
+ value: '1',
299
+ label: 'John Doe',
300
+ email: 'john@example.com',
301
+ avatar: 'https://i.pravatar.cc/150?img=1',
302
+ role: 'Admin'
303
+ },
304
+ {
305
+ value: '2',
306
+ label: 'Jane Smith',
307
+ email: 'jane@example.com',
308
+ avatar: 'https://i.pravatar.cc/150?img=2',
309
+ role: 'User'
310
+ },
311
+ {
312
+ value: '3',
313
+ label: 'Bob Johnson',
314
+ email: 'bob@example.com',
315
+ avatar: 'https://i.pravatar.cc/150?img=3',
316
+ role: 'Moderator'
317
+ }
318
+ ];
319
+
320
+ select.items = users;
321
+ select.optionTemplate = (item, index) => `
322
+ <div style="display: flex; align-items: center; gap: 12px; padding: 4px 0;">
323
+ <img
324
+ src="${item.avatar}"
325
+ alt="${item.label}"
326
+ style="width: 40px; height: 40px; border-radius: 50%; object-fit: cover;"
327
+ />
328
+ <div style="flex: 1;">
329
+ <div style="font-weight: 600; color: #1f2937;">${item.label}</div>
330
+ <div style="font-size: 13px; color: #6b7280;">${item.email}</div>
331
+ </div>
332
+ <span style="
333
+ padding: 4px 8px;
334
+ background: ${item.role === 'Admin' ? '#dbeafe' : '#f3f4f6'};
335
+ color: ${item.role === 'Admin' ? '#1e40af' : '#374151'};
336
+ border-radius: 12px;
337
+ font-size: 11px;
338
+ font-weight: 600;
339
+ ">${item.role}</span>
340
+ </div>
341
+ `;
342
+
343
+ // Example 3: Product selection with images and pricing
344
+ const products = [
345
+ {
346
+ value: 'p1',
347
+ label: 'Premium Laptop',
348
+ price: 1299.99,
349
+ stock: 15,
350
+ image: 'https://via.placeholder.com/60',
351
+ badge: 'Best Seller'
352
+ },
353
+ {
354
+ value: 'p2',
355
+ label: 'Wireless Mouse',
356
+ price: 29.99,
357
+ stock: 150,
358
+ image: 'https://via.placeholder.com/60',
359
+ badge: null
360
+ },
361
+ {
362
+ value: 'p3',
363
+ label: 'Mechanical Keyboard',
364
+ price: 89.99,
365
+ stock: 0,
366
+ image: 'https://via.placeholder.com/60',
367
+ badge: 'Out of Stock'
368
+ }
369
+ ];
370
+
371
+ select.items = products;
372
+ select.optionTemplate = (item, index) => `
373
+ <div style="display: flex; align-items: center; gap: 12px; opacity: ${item.stock === 0 ? '0.5' : '1'};">
374
+ <img
375
+ src="${item.image}"
376
+ alt="${item.label}"
377
+ style="width: 60px; height: 60px; border-radius: 8px; object-fit: cover; border: 1px solid #e5e7eb;"
378
+ />
379
+ <div style="flex: 1;">
380
+ <div style="display: flex; align-items: center; gap: 8px;">
381
+ <span style="font-weight: 600; color: #1f2937;">${item.label}</span>
382
+ ${item.badge ? `
383
+ <span style="
384
+ padding: 2px 6px;
385
+ background: ${item.badge === 'Best Seller' ? '#dcfce7' : '#fee2e2'};
386
+ color: ${item.badge === 'Best Seller' ? '#166534' : '#991b1b'};
387
+ border-radius: 4px;
388
+ font-size: 10px;
389
+ font-weight: 600;
390
+ ">${item.badge}</span>
391
+ ` : ''}
392
+ </div>
393
+ <div style="margin-top: 4px; display: flex; justify-content: space-between; align-items: center;">
394
+ <span style="font-size: 16px; font-weight: 700; color: #059669;">$${item.price.toFixed(2)}</span>
395
+ <span style="font-size: 12px; color: #6b7280;">${item.stock > 0 ? `${item.stock} in stock` : 'Out of stock'}</span>
396
+ </div>
397
+ </div>
398
+ </div>
399
+ `;
400
+
401
+ // Example 4: Status indicators with conditional styling
402
+ const tasks = [
403
+ { value: 't1', label: 'Design Homepage', status: 'completed', priority: 'high', assignee: 'John' },
404
+ { value: 't2', label: 'API Integration', status: 'in-progress', priority: 'high', assignee: 'Jane' },
405
+ { value: 't3', label: 'Write Documentation', status: 'pending', priority: 'medium', assignee: 'Bob' },
406
+ { value: 't4', label: 'Bug Fixes', status: 'in-progress', priority: 'low', assignee: 'Alice' }
407
+ ];
408
+
409
+ const statusColors = {
410
+ 'completed': { bg: '#dcfce7', color: '#166534', icon: '✓' },
411
+ 'in-progress': { bg: '#dbeafe', color: '#1e40af', icon: '⟳' },
412
+ 'pending': { bg: '#fef3c7', color: '#92400e', icon: '○' }
413
+ };
414
+
415
+ const priorityColors = {
416
+ 'high': '#ef4444',
417
+ 'medium': '#f59e0b',
418
+ 'low': '#10b981'
419
+ };
420
+
421
+ select.items = tasks;
422
+ select.optionTemplate = (item, index) => {
423
+ const status = statusColors[item.status];
424
+ return `
425
+ <div style="display: flex; align-items: center; gap: 10px; padding: 4px 0;">
426
+ <div style="
427
+ width: 24px;
428
+ height: 24px;
429
+ border-radius: 50%;
430
+ background: ${status.bg};
431
+ color: ${status.color};
432
+ display: flex;
433
+ align-items: center;
434
+ justify-content: center;
435
+ font-weight: bold;
436
+ ">${status.icon}</div>
437
+ <div style="flex: 1;">
438
+ <div style="font-weight: 600; color: #1f2937;">${item.label}</div>
439
+ <div style="font-size: 12px; color: #6b7280; margin-top: 2px;">
440
+ Assigned to ${item.assignee}
441
+ </div>
442
+ </div>
443
+ <div style="
444
+ width: 8px;
445
+ height: 8px;
446
+ border-radius: 50%;
447
+ background: ${priorityColors[item.priority]};
448
+ " title="${item.priority} priority"></div>
449
+ </div>
450
+ `;
451
+ };
452
+ ```
453
+
454
+ ### Comparison: When to Use Each Method
455
+
456
+ | Feature | Method 1: Object Arrays | Method 2: Custom Renderers |
457
+ |---------|------------------------|---------------------------|
458
+ | **Setup Complexity** | ⭐ Simple | ⭐⭐ Moderate |
459
+ | **Rendering Speed** | ⭐⭐⭐ Fastest | ⭐⭐ Fast |
460
+ | **Visual Customization** | ⭐⭐ Limited | ⭐⭐⭐ Unlimited |
461
+ | **Use Case** | Standard dropdowns | Rich, complex UIs |
462
+ | **Code Amount** | Minimal | More code |
463
+ | **TypeScript Support** | ⭐⭐⭐ Full | ⭐⭐⭐ Full |
464
+ | **Performance (1M items)** | ⭐⭐⭐ Excellent | ⭐⭐ Good |
465
+ | **Learning Curve** | ⭐ Easy | ⭐⭐ Medium |
466
+
467
+ **Best Practices**:
468
+
469
+ ✅ **Use Method 1 (Object Arrays) when**:
470
+ - You need simple text-based options
471
+ - Performance is critical (millions of items)
472
+ - You want minimal code
473
+ - Built-in search/filter is sufficient
474
+
475
+ ✅ **Use Method 2 (Custom Renderers) when**:
476
+ - You need images, icons, or badges
477
+ - Options require multiple lines of text
478
+ - Custom styling/layout is important
479
+ - Conditional rendering based on data
480
+ - Rich user experience is priority
481
+
482
+ ### Combining Both Methods
483
+
484
+ You can start with Method 1 and add Method 2 later as your UI evolves:
485
+
486
+ ```javascript
487
+ // Start simple
488
+ select.items = ['Option 1', 'Option 2', 'Option 3'];
489
+
490
+ // Later, add custom rendering without changing items
491
+ select.optionTemplate = (item, index) => `
492
+ <div style="padding: 8px; background: ${index % 2 ? '#f9fafb' : 'white'};">
493
+ <strong>${item.label || item}</strong>
494
+ </div>
495
+ `;
496
+ ```
497
+
498
+ ---
499
+
174
500
  ### Events
175
501
 
176
502
  ```typescript