[READ-ONLY] Mirror of https://github.com/probablykasper/ferrum. Music library app for Mac, Linux and Windows ferrum.kasper.space
electron linux macos music music-library music-player napi windows
0

Configure Feed

Select the types of activity you want to include in your feed.

Fix scrollbar overlapping

Kasper (May 28, 2025, 12:41 PM +0200) fb49b852 bf811b78

+121 -103
+121 -103
src/components/TrackList.svelte
··· 269 269 270 270 let grid: HTMLRevoGridElement 271 271 272 - type ColumnRefined = ColumnRegular & { prop: string } 273 - type ColumnDef = ColumnRefined & 274 - ( 272 + type ColumnRefined = ColumnRegular & { prop: string; left_offset: number } 273 + type ColumnDef = ColumnRegular & { prop: string } & ( 275 274 | { 276 275 width_px: number 277 276 width_pct?: undefined ··· 389 388 'dateAdded', 390 389 'year', 391 390 ] 391 + if (view_options.columns.length === 0) { 392 + view_options.columns = [...default_columns] 393 + } 392 394 let columns: ColumnRefined[] = load_columns() 393 395 onMount(() => (columns = load_columns())) 394 396 function load_columns(): ColumnRefined[] { ··· 403 405 const total_percent_pct = columns.reduce((sum, col) => sum + (col.width_pct || 0), 0) 404 406 const container_width = grid?.clientWidth ?? total_fixed_width 405 407 const total_percent_width = container_width - total_fixed_width 408 + let left_offset = 0 406 409 return columns.map((col) => { 407 410 let size: number 408 411 if (col.width_px !== undefined) { ··· 410 413 } else { 411 414 size = (col.width_pct / total_percent_pct) * total_percent_width 412 415 } 416 + const this_left_offset = left_offset 417 + left_offset += size 413 418 return { 414 419 ...col, 415 420 name: col.name === 'Image' ? '' : col.name, 416 421 size, 422 + left_offset: this_left_offset, 423 + columnTemplate() { 424 + return null 425 + }, 417 426 columnProperties() { 418 427 return { class: col.prop } 419 428 }, ··· 606 615 element: row, 607 616 } 608 617 } 609 - function get_header_col(e: Event) { 610 - if (!(e.target instanceof Element)) { 611 - return null 612 - } 613 - const row = e.target?.closest('.rgHeaderCell[data-rgcol]') 614 - const row_index_str = parseInt(row?.getAttribute('data-rgcol') ?? '') 615 - if (!row || !Number.isInteger(row_index_str)) { 616 - return null 617 - } 618 - return { 619 - index: row_index_str, 620 - element: row, 621 - } 622 - } 623 618 624 619 onMount(() => { 625 620 grid.addEventListener('aftergridinit', tracklist_actions.focus) ··· 637 632 columns = load_columns() 638 633 }} 639 634 /> 640 - <!-- svelte-ignore a11y_no_static_element_interactions --> 641 - <revo-grid 642 - bind:this={grid} 643 - class="main-focus-element grid grow" 644 - {@attach (grid: HTMLRevoGridElement) => { 645 - grid.setAttribute('theme', 'darkCompact') 646 - grid.readonly = true 647 - grid.rowSize = item_height 648 - grid.style.lineHeight = 'item_height' + 'px' 649 - grid.canFocus = false 650 - grid.resize = false 651 - grid.canDrag = false 652 - grid.canMoveColumns = false 653 - grid.hideAttribution = true 654 - grid.rowClass = 'row_class' 655 - }} 656 - onkeydown={(e: KeyboardEvent & { currentTarget: Element }) => { 657 - scroll_container_keydown(e) 658 - keydown(e) 659 - }} 660 - onmousedown={(e: MouseEvent) => { 661 - if (e.target instanceof HTMLElement && e.target.tagName === 'REVOGR-VIEWPORT-SCROLL') { 662 - selection.clear() 663 - return 664 - } 665 - const row = get_row(e) 666 - if (row) { 667 - selection.handle_mousedown(e, row.index) 668 - setTimeout(() => { 669 - // Prevent cell focus 670 - grid.focus() 671 - }) 672 - } 673 - }} 674 - onclick={(e: MouseEvent) => { 675 - const row = get_row(e) 676 - if (row) { 677 - selection.handle_click(e, row.index) 678 - } 679 - }} 680 - ondblclick={(e: MouseEvent) => { 681 - const row = get_row(e) 682 - if (row) { 683 - double_click(e, row.index) 684 - } 685 - }} 686 - oncontextmenu={(e: MouseEvent) => { 687 - const row = get_row(e) 688 - if (row) { 689 - selection.handle_contextmenu(e, row.index) 690 - return 691 - } 692 - const header_col = get_header_col(e) 693 - if (header_col) { 694 - on_column_context_menu() 695 - } 696 - }} 697 - ondragstart={on_drag_start} 698 - ondragover={(e: DragEvent) => { 699 - const row = get_row(e) 700 - if (row) { 701 - on_drag_over(e, row.element, row.index) 702 - } 703 - }} 704 - ondrop={drop_handler} 705 - ondragend={() => { 706 - drag_to_index = null 707 - }} 708 - ondragleave={() => { 709 - drag_to_index = null 710 - }} 711 - > 712 - <div class="relative" slot="data-rgCol-rgRow"> 713 - <div class="drag-line" class:hidden={drag_to_index === null} bind:this={drag_line}></div> 635 + 636 + <div class="grid-container flex grow flex-col"> 637 + <div 638 + class="row grid-header border-b border-b-slate-500/30" 639 + class:desc={$sort_desc} 640 + role="row" 641 + oncontextmenu={on_column_context_menu} 642 + > 643 + {#each columns as column, i} 644 + <div 645 + class="rgCell {column.prop}" 646 + style:width="{column.size}px" 647 + style:translate="{column.left_offset}px 0" 648 + role="button" 649 + > 650 + {column.prop === 'image' ? '' : column.name} 651 + </div> 652 + {/each} 714 653 </div> 715 - </revo-grid> 654 + 655 + <!-- svelte-ignore a11y_no_static_element_interactions --> 656 + <revo-grid 657 + bind:this={grid} 658 + class="main-focus-element grid grow" 659 + {@attach (grid: HTMLRevoGridElement) => { 660 + grid.setAttribute('theme', 'darkCompact') 661 + grid.readonly = true 662 + grid.rowSize = item_height 663 + grid.style.lineHeight = 'item_height' + 'px' 664 + grid.canFocus = false 665 + grid.resize = false 666 + grid.canDrag = false 667 + grid.canMoveColumns = false 668 + grid.hideAttribution = true 669 + grid.rowClass = 'row_class' 670 + }} 671 + onkeydown={(e: KeyboardEvent & { currentTarget: Element }) => { 672 + scroll_container_keydown(e) 673 + keydown(e) 674 + }} 675 + onmousedown={(e: MouseEvent) => { 676 + if (e.target instanceof HTMLElement && e.target.tagName === 'REVOGR-VIEWPORT-SCROLL') { 677 + selection.clear() 678 + return 679 + } 680 + const row = get_row(e) 681 + if (row) { 682 + selection.handle_mousedown(e, row.index) 683 + setTimeout(() => { 684 + // Prevent cell focus 685 + grid.focus() 686 + }) 687 + } 688 + }} 689 + onclick={(e: MouseEvent) => { 690 + const row = get_row(e) 691 + if (row) { 692 + selection.handle_click(e, row.index) 693 + } 694 + }} 695 + ondblclick={(e: MouseEvent) => { 696 + const row = get_row(e) 697 + if (row) { 698 + double_click(e, row.index) 699 + } 700 + }} 701 + oncontextmenu={(e: MouseEvent) => { 702 + const row = get_row(e) 703 + if (row) { 704 + selection.handle_contextmenu(e, row.index) 705 + } 706 + }} 707 + ondragstart={on_drag_start} 708 + ondragover={(e: DragEvent) => { 709 + const row = get_row(e) 710 + if (row) { 711 + on_drag_over(e, row.element, row.index) 712 + } 713 + }} 714 + ondrop={drop_handler} 715 + ondragend={() => (drag_to_index = null)} 716 + ondragleave={() => (drag_to_index = null)} 717 + > 718 + <div class="relative" slot="data-rgCol-rgRow"> 719 + <div class="drag-line" class:hidden={drag_to_index === null} bind:this={drag_line}></div> 720 + </div> 721 + </revo-grid> 722 + </div> 716 723 717 724 <!-- <div 718 725 bind:this={tracklist_element} ··· 769 776 </div> --> 770 777 771 778 <style lang="sass"> 772 - .grid :global 773 - outline: none 774 - min-height: 50px 775 - .header-rgRow 779 + .grid-header 780 + position: relative 781 + height: 24px 782 + line-height: 24px 783 + font-size: 12px 784 + flex-shrink: 0 785 + .rgCell 776 786 height: 24px 777 - line-height: 24px 778 - font-size: 12px 779 - box-shadow: none 780 - font-weight: 400 781 - .rgHeaderCell .resizable 787 + top: 0 788 + left: 0 789 + position: absolute 790 + box-sizing: border-box 791 + height: 100% 792 + overflow: hidden 793 + text-overflow: ellipsis 794 + white-space: nowrap 795 + .grid-container :global 796 + revo-grid 797 + outline: none 798 + min-height: 50px 799 + revogr-header 782 800 display: none 783 - .rgCell, .rgHeaderCell 801 + .rgCell 784 802 padding: 0 5px 785 803 &:first-child 786 804 padding-left: 10px ··· 835 853 .playing.selected > .index 836 854 // #ffffff 837 855 background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23ffffff'%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3Cpath d='M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z'/%3E%3C/svg%3E") 838 - .grid:focus-within :global 856 + .grid-container:focus-within :global 839 857 .rgRow.selected 840 858 background-color: hsla(var(--hue), 70%, 46%, 1) 841 859