@seed-ship/mcp-ui-solid 4.3.1 → 4.3.3

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.
@@ -345,6 +345,7 @@ function TableRenderer(props: {
345
345
  setSortDir('asc')
346
346
  }
347
347
  setClientPage(0)
348
+ setProgressivePages(1)
348
349
  }
349
350
 
350
351
  const sortedRows = createMemo(() => {
@@ -374,22 +375,70 @@ function TableRenderer(props: {
374
375
  return sortDir() === 'asc' ? '\u2191' : '\u2193'
375
376
  }
376
377
 
377
- // ─── Client-side pagination (v4.0.4) ─────────────────────
378
+ // ─── Client-side search filter (v4.3.3) ─────────────────────
379
+ const [searchQuery, setSearchQuery] = createSignal('')
380
+ const [debouncedQuery, setDebouncedQuery] = createSignal('')
381
+ let searchTimer: ReturnType<typeof setTimeout> | null = null
382
+
383
+ const isSearchable = () => tableParams.searchable === true || (tableParams.searchable !== false && allRows().length > 10)
384
+ const searchPlaceholder = () => tableParams.searchPlaceholder || 'Rechercher dans le tableau...'
385
+
386
+ const handleSearch = (value: string) => {
387
+ setSearchQuery(value)
388
+ if (searchTimer) clearTimeout(searchTimer)
389
+ searchTimer = setTimeout(() => {
390
+ setDebouncedQuery(value)
391
+ setClientPage(0)
392
+ setProgressivePages(1)
393
+ }, 200)
394
+ }
395
+
396
+ /** Normalize string for accent-insensitive matching */
397
+ const normalize = (s: string) => s.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase()
398
+
399
+ const filteredRows = createMemo(() => {
400
+ const q = normalize(debouncedQuery())
401
+ if (!q) return sortedRows()
402
+ const cols = columns()
403
+ return sortedRows().filter((row: any) =>
404
+ cols.some((col: any) => {
405
+ const val = row[col.key]
406
+ if (val == null) return false
407
+ return normalize(String(val)).includes(q)
408
+ })
409
+ )
410
+ })
411
+
412
+ // ─── Client-side pagination (v4.0.4, progressive mode v4.3.2) ─────
378
413
  const clientPageSize = () => tableParams.pageSize ?? 25
379
414
  const hasServerPagination = () => !!tableParams.pagination
415
+ const isProgressiveMode = () => !!tableParams.showAllLabel
380
416
  const needsClientPagination = () =>
381
- !hasServerPagination() && clientPageSize() > 0 && sortedRows().length > clientPageSize()
417
+ !hasServerPagination() && clientPageSize() > 0 && filteredRows().length > clientPageSize()
382
418
  const [clientPage, setClientPage] = createSignal(tableParams.initialPage ?? 0)
383
- const clientTotalPages = () => needsClientPagination() ? Math.ceil(sortedRows().length / clientPageSize()) : 1
419
+ // Progressive mode: track how many pages to show (append)
420
+ const [progressivePages, setProgressivePages] = createSignal(1)
421
+ const clientTotalPages = () => needsClientPagination() ? Math.ceil(filteredRows().length / clientPageSize()) : 1
384
422
  const clientVisibleRows = createMemo(() => {
385
- if (!needsClientPagination()) return sortedRows()
423
+ if (!needsClientPagination()) return filteredRows()
424
+ if (isProgressiveMode()) {
425
+ // Progressive: show first N * pageSize rows
426
+ return filteredRows().slice(0, progressivePages() * clientPageSize())
427
+ }
386
428
  const start = clientPage() * clientPageSize()
387
- return sortedRows().slice(start, start + clientPageSize())
429
+ return filteredRows().slice(start, start + clientPageSize())
388
430
  })
389
- const clientRangeStart = () => needsClientPagination() ? clientPage() * clientPageSize() + 1 : 1
431
+ const clientRangeStart = () => needsClientPagination()
432
+ ? (isProgressiveMode() ? 1 : clientPage() * clientPageSize() + 1)
433
+ : 1
390
434
  const clientRangeEnd = () => needsClientPagination()
391
- ? Math.min((clientPage() + 1) * clientPageSize(), sortedRows().length)
392
- : sortedRows().length
435
+ ? (isProgressiveMode()
436
+ ? Math.min(progressivePages() * clientPageSize(), filteredRows().length)
437
+ : Math.min((clientPage() + 1) * clientPageSize(), filteredRows().length))
438
+ : filteredRows().length
439
+ const progressiveHasMore = () => isProgressiveMode() && needsClientPagination() && progressivePages() < clientTotalPages()
440
+ const progressiveRemaining = () => filteredRows().length - progressivePages() * clientPageSize()
441
+ const showMoreLabel = () => tableParams.showAllLabel || 'Show more'
393
442
 
394
443
  // ─── Virtualization ──────────────────────────────────────
395
444
  const [virtualizer, setVirtualizer] = createSignal<any>(null)
@@ -625,6 +674,33 @@ function TableRenderer(props: {
625
674
  </h3>
626
675
  </Show>
627
676
 
677
+ {/* Search input (v4.3.3) */}
678
+ <Show when={isSearchable()}>
679
+ <div class="relative mb-3">
680
+ <span class="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 pointer-events-none text-sm">{'\uD83D\uDD0D'}</span>
681
+ <input
682
+ type="text"
683
+ value={searchQuery()}
684
+ onInput={(e) => handleSearch(e.currentTarget.value)}
685
+ placeholder={searchPlaceholder()}
686
+ class="w-full pl-8 pr-8 py-1.5 text-sm border border-gray-200 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-900 dark:text-white placeholder-gray-400 focus:border-blue-400 focus:ring-1 focus:ring-blue-400 outline-none"
687
+ />
688
+ <Show when={searchQuery()}>
689
+ <button
690
+ type="button"
691
+ onClick={() => { handleSearch(''); setSearchQuery(''); setDebouncedQuery('') }}
692
+ class="absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 text-sm"
693
+ aria-label="Clear search"
694
+ >&times;</button>
695
+ </Show>
696
+ </div>
697
+ <Show when={debouncedQuery() && filteredRows().length !== sortedRows().length}>
698
+ <p class="text-xs text-gray-500 dark:text-gray-400 mb-2">
699
+ {filteredRows().length} result{filteredRows().length !== 1 ? 's' : ''} on {sortedRows().length}
700
+ </p>
701
+ </Show>
702
+ </Show>
703
+
628
704
  <div
629
705
  ref={scrollContainerRef}
630
706
  class="overflow-x-auto"
@@ -685,11 +761,11 @@ function TableRenderer(props: {
685
761
  </div>
686
762
  </Show>
687
763
 
688
- {/* Client-side auto-pagination (v4.0.4) */}
689
- <Show when={needsClientPagination()}>
764
+ {/* Client-side paged pagination (v4.0.4) */}
765
+ <Show when={needsClientPagination() && !isProgressiveMode()}>
690
766
  <div class="mt-3 flex items-center justify-between text-xs text-gray-500 dark:text-gray-400">
691
767
  <span>
692
- Showing {clientRangeStart()}&ndash;{clientRangeEnd()} of {allRows().length.toLocaleString('fr-FR')}
768
+ Showing {clientRangeStart()}&ndash;{clientRangeEnd()} of {filteredRows().length.toLocaleString('fr-FR')}
693
769
  </span>
694
770
  <div class="flex items-center gap-1">
695
771
  <button
@@ -710,6 +786,23 @@ function TableRenderer(props: {
710
786
  </div>
711
787
  </div>
712
788
  </Show>
789
+
790
+ {/* Client-side progressive pagination (v4.3.2) */}
791
+ <Show when={needsClientPagination() && isProgressiveMode()}>
792
+ <div class="mt-3 flex flex-col items-center gap-2 text-xs text-gray-500 dark:text-gray-400">
793
+ <span>
794
+ {clientRangeStart()}&ndash;{clientRangeEnd()} of {filteredRows().length.toLocaleString('fr-FR')}
795
+ </span>
796
+ <Show when={progressiveHasMore()}>
797
+ <button
798
+ class="px-4 py-1.5 rounded-md bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 text-gray-700 dark:text-gray-300 transition-colors"
799
+ onClick={() => setProgressivePages(p => p + 1)}
800
+ >
801
+ {showMoreLabel()} ({Math.min(progressiveRemaining(), clientPageSize())} suivant{Math.min(progressiveRemaining(), clientPageSize()) > 1 ? 'es' : 'e'})
802
+ </button>
803
+ </Show>
804
+ </div>
805
+ </Show>
713
806
  </div>
714
807
  </div>
715
808
  </ExpandableWrapper>