mirror of
https://github.com/hensm/fx_cast.git
synced 2026-06-08 08:39:59 +00:00
Make receiver selection list scrollable/searchable
This commit is contained in:
13
ext/package-lock.json
generated
13
ext/package-lock.json
generated
@@ -11,6 +11,7 @@
|
||||
"esbuild": "^0.14.38",
|
||||
"esbuild-svelte": "^0.7.1",
|
||||
"fs-extra": "^10.1.0",
|
||||
"fuzzysort": "^2.0.3",
|
||||
"semver": "^7.3.7",
|
||||
"svelte": "^3.48.0",
|
||||
"svelte-preprocess": "^4.10.6",
|
||||
@@ -3359,6 +3360,12 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/fuzzysort": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/fuzzysort/-/fuzzysort-2.0.3.tgz",
|
||||
"integrity": "sha512-ridbZloOhbVCom7MDF//Blt4N6d6L4uWNEiBuYBx4Qr+G55A9qItgsAqrRZGgs8q+mFpyBbwi5FL+FWUQPJCew==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/fx-runner": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/fx-runner/-/fx-runner-1.2.0.tgz",
|
||||
@@ -10296,6 +10303,12 @@
|
||||
"integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
|
||||
"dev": true
|
||||
},
|
||||
"fuzzysort": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/fuzzysort/-/fuzzysort-2.0.3.tgz",
|
||||
"integrity": "sha512-ridbZloOhbVCom7MDF//Blt4N6d6L4uWNEiBuYBx4Qr+G55A9qItgsAqrRZGgs8q+mFpyBbwi5FL+FWUQPJCew==",
|
||||
"dev": true
|
||||
},
|
||||
"fx-runner": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/fx-runner/-/fx-runner-1.2.0.tgz",
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"esbuild": "^0.14.38",
|
||||
"esbuild-svelte": "^0.7.1",
|
||||
"fs-extra": "^10.1.0",
|
||||
"fuzzysort": "^2.0.3",
|
||||
"semver": "^7.3.7",
|
||||
"svelte": "^3.48.0",
|
||||
"svelte-preprocess": "^4.10.6",
|
||||
|
||||
16
ext/src/ui/assets/photon_cancel.svg
Normal file
16
ext/src/ui/assets/photon_cancel.svg
Normal file
@@ -0,0 +1,16 @@
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<style>
|
||||
path {
|
||||
fill: rgba(12, 12, 13, .8);
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
path {
|
||||
fill: rgba(249, 249, 250, .8);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<path d="M6.586 8l-2.293 2.293a1 1 0 0 0 1.414 1.414L8 9.414l2.293 2.293a1 1 0 0 0 1.414-1.414L9.414 8l2.293-2.293a1 1 0 1 0-1.414-1.414L8 6.586 5.707 4.293a1 1 0 0 0-1.414 1.414L6.586 8zM8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 761 B |
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { afterUpdate, onDestroy, onMount, tick } from "svelte";
|
||||
import fuzzysort from "fuzzysort";
|
||||
|
||||
import messaging, { Message, Port } from "../../messaging";
|
||||
import options, { Options } from "../../lib/options";
|
||||
@@ -321,8 +322,47 @@
|
||||
function openOptionsPage() {
|
||||
browser.runtime.openOptionsPage();
|
||||
}
|
||||
|
||||
/** Search input element. */
|
||||
let searchInput: HTMLInputElement | undefined;
|
||||
/** Current search term. */
|
||||
let searchTerm: string | undefined;
|
||||
let isSearching = false;
|
||||
/** Results of current search term. */
|
||||
let searchResults: Fuzzysort.KeyResults<ReceiverDevice> | undefined;
|
||||
|
||||
async function handleKeyDown(ev: KeyboardEvent) {
|
||||
if (ev.key === "Escape") {
|
||||
handleSearchClear();
|
||||
return;
|
||||
}
|
||||
if (!isSearching && ev.key.length === 1) {
|
||||
isSearching = true;
|
||||
}
|
||||
|
||||
await tick();
|
||||
searchInput?.focus();
|
||||
}
|
||||
function handleSearchInput() {
|
||||
// Clear search on empty string
|
||||
if (!searchTerm) {
|
||||
handleSearchClear();
|
||||
return;
|
||||
}
|
||||
|
||||
searchResults = fuzzysort.go(searchTerm, devices, {
|
||||
key: "friendlyName"
|
||||
});
|
||||
}
|
||||
|
||||
function handleSearchClear() {
|
||||
isSearching = false;
|
||||
searchTerm = undefined;
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={handleKeyDown} />
|
||||
|
||||
{#if !isBridgeCompatible}
|
||||
<div class="banner banner--warn">
|
||||
{_("popupBridgeErrorBanner")}
|
||||
@@ -379,8 +419,56 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if isSearching}
|
||||
<div class="search">
|
||||
<input
|
||||
type="text"
|
||||
class="search-input"
|
||||
bind:this={searchInput}
|
||||
bind:value={searchTerm}
|
||||
on:input={handleSearchInput}
|
||||
title={_("popupSearch")}
|
||||
/>
|
||||
<button
|
||||
class="search-clear ghost"
|
||||
title={_("popupSearchClear")}
|
||||
on:click={handleSearchClear}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<ul class="receiver-list">
|
||||
{#if !devices.length}
|
||||
{#if searchTerm && searchResults}
|
||||
{#if !searchResults.length}
|
||||
<div class="receiver-list__not-found">
|
||||
No devices found for "{searchTerm}"
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#each searchResults as result}
|
||||
{@const device = devices.find(
|
||||
device => device.id === result.obj.id
|
||||
)}
|
||||
|
||||
{#if device}
|
||||
<Receiver
|
||||
{opts}
|
||||
{port}
|
||||
{device}
|
||||
{result}
|
||||
{connectedSessionIds}
|
||||
{isMediaTypeAvailable}
|
||||
isAnyMediaTypeAvailable={availableMediaTypes !==
|
||||
ReceiverSelectorMediaType.None &&
|
||||
isDeviceCompatible(mediaType, device)}
|
||||
isAnyConnecting={isConnecting}
|
||||
bind:lastMenuShownDeviceId
|
||||
on:cast={ev => onReceiverCast(ev.detail.device)}
|
||||
on:stop={ev => onReceiverStop(ev.detail.device)}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
{:else if !devices.length}
|
||||
<div class="receiver-list__not-found">
|
||||
{_("popupNoReceiversFound")}
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher, onMount } from "svelte";
|
||||
import fuzzysort from "fuzzysort";
|
||||
|
||||
import type { Options } from "../../lib/options";
|
||||
|
||||
@@ -39,6 +40,9 @@
|
||||
export let device: ReceiverDevice;
|
||||
export let connectedSessionIds: string[];
|
||||
|
||||
/** Result object if this receiver is displayed in a search results list. */
|
||||
export let result: Nullable<Fuzzysort.KeyResult<ReceiverDevice>> = null;
|
||||
|
||||
export let opts: Nullable<Options>;
|
||||
|
||||
/** Current receiver application (if available) */
|
||||
@@ -402,7 +406,12 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<li class="receiver" bind:this={receiverElement} on:contextmenu={onContextMenu}>
|
||||
<li
|
||||
class="receiver"
|
||||
class:receiver--result={!!result}
|
||||
bind:this={receiverElement}
|
||||
on:contextmenu={onContextMenu}
|
||||
>
|
||||
<img
|
||||
class="receiver__icon"
|
||||
src="icons/{device.capabilities & ReceiverDeviceCapabilities.VIDEO_OUT
|
||||
@@ -414,7 +423,11 @@
|
||||
/>
|
||||
<div class="receiver__details">
|
||||
<div class="receiver__name">
|
||||
{device.friendlyName}
|
||||
{#if result}
|
||||
{@html fuzzysort.highlight(result)}
|
||||
{:else}
|
||||
{device.friendlyName}
|
||||
{/if}
|
||||
</div>
|
||||
{#if application && !application.isIdleScreen}
|
||||
<div class="receiver__status">
|
||||
|
||||
@@ -83,9 +83,36 @@ body {
|
||||
margin-inline-start: 0.5em;
|
||||
}
|
||||
|
||||
.search {
|
||||
display: flex;
|
||||
padding: 1em;
|
||||
position: relative;
|
||||
margin-top: -1px;
|
||||
}
|
||||
.search-input {
|
||||
width: 100%;
|
||||
}
|
||||
.search-clear {
|
||||
background-color: transparent !important;
|
||||
background-image: url("../../assets/photon_cancel.svg");
|
||||
background-size: 50%;
|
||||
margin-right: 1em;
|
||||
opacity: 0.5;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
.search-clear:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.receiver-list {
|
||||
list-style: none;
|
||||
margin: initial;
|
||||
height: auto;
|
||||
max-height: 500px;
|
||||
overflow-y: auto;
|
||||
padding: 0 1em;
|
||||
padding-bottom: 0.25em;
|
||||
}
|
||||
@@ -108,6 +135,9 @@ body {
|
||||
padding: 0.75em 0;
|
||||
position: relative;
|
||||
}
|
||||
.receiver--result .receiver__name > b {
|
||||
font-weight: 600;
|
||||
}
|
||||
.receiver:not(:last-child) {
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user