@smilodon/core 1.0.9 → 1.0.11

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/dist/index.umd.js CHANGED
@@ -869,6 +869,13 @@
869
869
  preloadAdjacent: true,
870
870
  scrollRestoration: 'auto',
871
871
  },
872
+ expandable: {
873
+ enabled: false,
874
+ collapsedHeight: '300px',
875
+ expandedHeight: '500px',
876
+ expandLabel: 'Show more',
877
+ collapseLabel: 'Show less',
878
+ },
872
879
  callbacks: {},
873
880
  enabled: true,
874
881
  searchable: false,
@@ -979,6 +986,7 @@
979
986
  lastScrollPosition: 0,
980
987
  lastNotifiedQuery: null,
981
988
  lastNotifiedResultCount: 0,
989
+ isExpanded: false,
982
990
  };
983
991
  // Create DOM structure
984
992
  this._container = this._createContainer();
@@ -1271,17 +1279,39 @@
1271
1279
  right: 0;
1272
1280
  margin-top: 4px;
1273
1281
  max-height: 300px;
1274
- overflow-y: auto;
1282
+ display: flex;
1283
+ flex-direction: column;
1275
1284
  background: var(--select-dropdown-bg, white);
1276
1285
  border: 1px solid var(--select-dropdown-border, #ccc);
1277
1286
  border-radius: var(--select-border-radius, 4px);
1278
1287
  box-shadow: var(--select-dropdown-shadow, 0 4px 6px rgba(0,0,0,0.1));
1279
1288
  z-index: var(--select-dropdown-z-index, 1000);
1289
+ transition: max-height 0.3s ease-in-out;
1280
1290
  }
1281
1291
 
1282
1292
  .options-container {
1283
1293
  position: relative;
1284
1294
  transition: opacity 0.2s ease-in-out;
1295
+ overflow-y: auto;
1296
+ flex: 1;
1297
+ }
1298
+
1299
+ .expand-toggle {
1300
+ padding: 8px;
1301
+ text-align: center;
1302
+ background: #f9fafb;
1303
+ border-top: 1px solid #e5e7eb;
1304
+ cursor: pointer;
1305
+ font-size: 12px;
1306
+ color: #6b7280;
1307
+ font-weight: 500;
1308
+ transition: background 0.2s;
1309
+ flex-shrink: 0;
1310
+ }
1311
+
1312
+ .expand-toggle:hover {
1313
+ background: #f3f4f6;
1314
+ color: #374151;
1285
1315
  }
1286
1316
 
1287
1317
  .option {
@@ -1544,6 +1574,13 @@
1544
1574
  return;
1545
1575
  this._state.isOpen = true;
1546
1576
  this._dropdown.style.display = 'block';
1577
+ // Set initial height based on expandable config
1578
+ if (this._config.expandable.enabled) {
1579
+ const height = this._state.isExpanded
1580
+ ? (this._config.expandable.expandedHeight || '500px')
1581
+ : (this._config.expandable.collapsedHeight || '300px');
1582
+ this._dropdown.style.maxHeight = height;
1583
+ }
1547
1584
  this._input.setAttribute('aria-expanded', 'true');
1548
1585
  this._updateArrowRotation();
1549
1586
  // Clear search query when opening to show all options
@@ -2268,24 +2305,33 @@
2268
2305
  });
2269
2306
  this._optionsContainer.appendChild(header);
2270
2307
  group.options.forEach(item => {
2271
- this._renderSingleOption(item, getValue, getLabel);
2308
+ // Find original index for correct ID generation and selection
2309
+ const index = this._state.loadedItems.indexOf(item);
2310
+ if (index !== -1) {
2311
+ this._renderSingleOption(item, index, getValue, getLabel);
2312
+ }
2272
2313
  });
2273
2314
  });
2274
2315
  }
2275
2316
  else {
2276
2317
  // Normal rendering (flat list or filtered)
2277
- const itemsToRender = query
2278
- ? this._state.loadedItems.filter((item) => {
2318
+ let hasRenderedItems = false;
2319
+ this._state.loadedItems.forEach((item, index) => {
2320
+ // Apply filter if query exists
2321
+ if (query) {
2279
2322
  try {
2280
2323
  const label = String(getLabel(item)).toLowerCase();
2281
- return label.includes(query);
2324
+ if (!label.includes(query))
2325
+ return;
2282
2326
  }
2283
2327
  catch (e) {
2284
- return false;
2328
+ return;
2285
2329
  }
2286
- })
2287
- : this._state.loadedItems;
2288
- if (itemsToRender.length === 0 && !this._state.isBusy) {
2330
+ }
2331
+ hasRenderedItems = true;
2332
+ this._renderSingleOption(item, index, getValue, getLabel);
2333
+ });
2334
+ if (!hasRenderedItems && !this._state.isBusy) {
2289
2335
  const empty = document.createElement('div');
2290
2336
  empty.className = 'empty-state';
2291
2337
  if (query) {
@@ -2296,11 +2342,6 @@
2296
2342
  }
2297
2343
  this._optionsContainer.appendChild(empty);
2298
2344
  }
2299
- else {
2300
- itemsToRender.forEach((item) => {
2301
- this._renderSingleOption(item, getValue, getLabel);
2302
- });
2303
- }
2304
2345
  }
2305
2346
  // Append Busy Indicator if busy
2306
2347
  if (this._state.isBusy && this._config.busyBucket.enabled) {
@@ -2322,27 +2363,57 @@
2322
2363
  else if ((this._config.loadMore.enabled || this._config.infiniteScroll.enabled) && this._state.loadedItems.length > 0) {
2323
2364
  this._addLoadMoreTrigger();
2324
2365
  }
2366
+ // Render Expand Button if enabled
2367
+ if (this._config.expandable.enabled) {
2368
+ this._renderExpandButton();
2369
+ }
2325
2370
  }
2326
- _renderSingleOption(item, getValue, getLabel) {
2371
+ _renderExpandButton() {
2372
+ const button = document.createElement('div');
2373
+ button.className = 'expand-toggle';
2374
+ button.textContent = this._state.isExpanded
2375
+ ? (this._config.expandable.collapseLabel || 'Show less')
2376
+ : (this._config.expandable.expandLabel || 'Show more');
2377
+ button.addEventListener('click', (e) => {
2378
+ e.stopPropagation(); // Prevent closing dropdown
2379
+ this._toggleExpand();
2380
+ });
2381
+ this._dropdown.appendChild(button);
2382
+ }
2383
+ _toggleExpand() {
2384
+ this._state.isExpanded = !this._state.isExpanded;
2385
+ const height = this._state.isExpanded
2386
+ ? (this._config.expandable.expandedHeight || '500px')
2387
+ : (this._config.expandable.collapsedHeight || '300px');
2388
+ this._dropdown.style.maxHeight = height;
2389
+ // Re-render button to update label
2390
+ const existingButton = this._dropdown.querySelector('.expand-toggle');
2391
+ if (existingButton) {
2392
+ existingButton.textContent = this._state.isExpanded
2393
+ ? (this._config.expandable.collapseLabel || 'Show less')
2394
+ : (this._config.expandable.expandLabel || 'Show more');
2395
+ }
2396
+ }
2397
+ _renderSingleOption(item, index, getValue, getLabel) {
2327
2398
  const option = document.createElement('div');
2328
2399
  option.className = 'option';
2400
+ option.id = `${this._uniqueId}-option-${index}`;
2329
2401
  const value = getValue(item);
2330
2402
  const label = getLabel(item);
2331
2403
  option.textContent = label;
2332
2404
  option.dataset.value = String(value);
2405
+ option.dataset.index = String(index); // Also useful for debugging/selectors
2333
2406
  // Check if selected using selectedItems map
2334
- const isSelected = Array.from(this._state.selectedItems.values()).some(selectedItem => {
2335
- const selectedValue = getValue(selectedItem);
2336
- return selectedValue === value;
2337
- });
2407
+ const isSelected = this._state.selectedIndices.has(index);
2338
2408
  if (isSelected) {
2339
2409
  option.classList.add('selected');
2410
+ option.setAttribute('aria-selected', 'true');
2411
+ }
2412
+ else {
2413
+ option.setAttribute('aria-selected', 'false');
2340
2414
  }
2341
2415
  option.addEventListener('click', () => {
2342
- const index = this._state.loadedItems.indexOf(item);
2343
- if (index !== -1) {
2344
- this._selectOption(index);
2345
- }
2416
+ this._selectOption(index);
2346
2417
  });
2347
2418
  this._optionsContainer.appendChild(option);
2348
2419
  }