mirror of
https://github.com/coracle-social/coracle.git
synced 2024-09-29 00:10:52 +00:00
Use new wot score indicator, revert to one-off style, show loading when searching profiles
This commit is contained in:
parent
6fd36ccecc
commit
dde8e47f34
@ -8,6 +8,8 @@
|
|||||||
- [x] Add zap splits
|
- [x] Add zap splits
|
||||||
- [x] Add default platform split amount
|
- [x] Add default platform split amount
|
||||||
- [x] Add invite link generation
|
- [x] Add invite link generation
|
||||||
|
- [x] Use new web of trust display
|
||||||
|
- [x] Show loading when searching profiles
|
||||||
|
|
||||||
# 0.4.2
|
# 0.4.2
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@
|
|||||||
external
|
external
|
||||||
href="https://info.coracle.social">
|
href="https://info.coracle.social">
|
||||||
<img alt="App Logo" src={import.meta.env.VITE_LOGO_URL || "/images/logo.png"} class="w-12" />
|
<img alt="App Logo" src={import.meta.env.VITE_LOGO_URL || "/images/logo.png"} class="w-12" />
|
||||||
<h1 class="staatliches text-4xl leading-none">{appName}</h1>
|
<h1 class="staatliches text-[2.6em] leading-none -mb-[0.1em]">{appName}</h1>
|
||||||
</Anchor>
|
</Anchor>
|
||||||
<MenuDesktopItem path="/notes">Feed</MenuDesktopItem>
|
<MenuDesktopItem path="/notes">Feed</MenuDesktopItem>
|
||||||
{#if !$env.FORCE_GROUP && $env.FORCE_RELAYS.length === 0}
|
{#if !$env.FORCE_GROUP && $env.FORCE_RELAYS.length === 0}
|
||||||
|
@ -65,7 +65,7 @@
|
|||||||
{#if $searchTerm}
|
{#if $searchTerm}
|
||||||
<div
|
<div
|
||||||
class="absolute right-0 top-10 max-h-[70vh] w-96 overflow-auto rounded bg-cocoa shadow-2xl">
|
class="absolute right-0 top-10 max-h-[70vh] w-96 overflow-auto rounded bg-cocoa shadow-2xl">
|
||||||
<SearchResults term={searchTerm}>
|
<SearchResults showLoading term={searchTerm}>
|
||||||
<div slot="result" let:result class="px-4 py-2 transition-colors hover:bg-dark">
|
<div slot="result" let:result class="px-4 py-2 transition-colors hover:bg-dark">
|
||||||
{#if result.type === "topic"}
|
{#if result.type === "topic"}
|
||||||
#{result.topic.name}
|
#{result.topic.name}
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {nip19} from "nostr-tools"
|
import {nip19} from "nostr-tools"
|
||||||
import {debounce, throttle} from "throttle-debounce"
|
import {throttle} from "throttle-debounce"
|
||||||
import {createEventDispatcher} from "svelte"
|
import {createEventDispatcher} from "svelte"
|
||||||
import {last, partition, whereEq} from "ramda"
|
import {last, partition, whereEq} from "ramda"
|
||||||
import PersonBadge from "src/app/shared/PersonBadge.svelte"
|
import PersonBadge from "src/app/shared/PersonBadge.svelte"
|
||||||
import ContentEditable from "src/partials/ContentEditable.svelte"
|
import ContentEditable from "src/partials/ContentEditable.svelte"
|
||||||
import Suggestions from "src/partials/Suggestions.svelte"
|
import Suggestions from "src/partials/Suggestions.svelte"
|
||||||
import {
|
import {
|
||||||
load,
|
|
||||||
follows,
|
follows,
|
||||||
derivePerson,
|
derivePerson,
|
||||||
displayPerson,
|
displayPerson,
|
||||||
searchableRelays,
|
searchableRelays,
|
||||||
getPubkeyHints,
|
getPubkeyHints,
|
||||||
searchPeople,
|
searchPeople,
|
||||||
|
createPeopleLoader,
|
||||||
} from "src/engine"
|
} from "src/engine"
|
||||||
|
|
||||||
export let onSubmit
|
export let onSubmit
|
||||||
@ -24,6 +24,11 @@
|
|||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
|
const {loading: loadingPeople, load: loadPeople} = createPeopleLoader({
|
||||||
|
shouldLoad: (term: string) => term.startsWith("@"),
|
||||||
|
onEvent: () => applySearch(getInfo().word),
|
||||||
|
})
|
||||||
|
|
||||||
const pubkeyEncoder = {
|
const pubkeyEncoder = {
|
||||||
encode: pubkey => {
|
encode: pubkey => {
|
||||||
const relays = getPubkeyHints.limit(3).getHints(pubkey, "write")
|
const relays = getPubkeyHints.limit(3).getHints(pubkey, "write")
|
||||||
@ -37,16 +42,6 @@
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadPeople = debounce(500, search => {
|
|
||||||
if (search.length > 2 && search.startsWith("@")) {
|
|
||||||
load({
|
|
||||||
relays: $searchableRelays,
|
|
||||||
filters: [{kinds: [0], search, limit: 10}],
|
|
||||||
onEvent: () => applySearch(getInfo().word),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const applySearch = throttle(300, word => {
|
const applySearch = throttle(300, word => {
|
||||||
let results = []
|
let results = []
|
||||||
if (word.length > 1 && word.startsWith("@")) {
|
if (word.length > 1 && word.startsWith("@")) {
|
||||||
@ -273,7 +268,10 @@
|
|||||||
<slot name="addon" />
|
<slot name="addon" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Suggestions bind:this={suggestions} select={person => autocomplete({person})}>
|
<Suggestions
|
||||||
|
bind:this={suggestions}
|
||||||
|
select={person => autocomplete({person})}
|
||||||
|
loading={$loadingPeople}>
|
||||||
<div slot="item" let:item>
|
<div slot="item" let:item>
|
||||||
<PersonBadge inert pubkey={item.pubkey} />
|
<PersonBadge inert pubkey={item.pubkey} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -14,7 +14,6 @@
|
|||||||
deriveAdminKeyForGroup,
|
deriveAdminKeyForGroup,
|
||||||
publishGroupExitRequest,
|
publishGroupExitRequest,
|
||||||
publishGroupEntryRequest,
|
publishGroupEntryRequest,
|
||||||
deriveGroup,
|
|
||||||
deriveGroupStatus,
|
deriveGroupStatus,
|
||||||
resetGroupAccess,
|
resetGroupAccess,
|
||||||
} from "src/engine"
|
} from "src/engine"
|
||||||
|
@ -254,7 +254,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex min-w-0 flex-grow flex-col gap-2">
|
<div class="flex min-w-0 flex-grow flex-col gap-2">
|
||||||
<div class="flex min-w-0 flex-col items-start justify-between sm:flex-row">
|
<div class="flex min-w-0 flex-col items-start justify-between sm:flex-row">
|
||||||
<Anchor type="unstyled" class="mr-4 min-w-0 text-lg font-bold" on:click={showPerson}>
|
<Anchor type="unstyled" class="mr-4 min-w-0" on:click={showPerson}>
|
||||||
<PersonName pubkey={event.pubkey} />
|
<PersonName pubkey={event.pubkey} />
|
||||||
</Anchor>
|
</Anchor>
|
||||||
<Anchor
|
<Anchor
|
||||||
|
@ -1,29 +1,76 @@
|
|||||||
|
<style>
|
||||||
|
.wot-background {
|
||||||
|
fill: transparent;
|
||||||
|
stroke: var(--mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wot-highlight {
|
||||||
|
fill: transparent;
|
||||||
|
stroke-width: 1;
|
||||||
|
stroke-dasharray: 100 100;
|
||||||
|
transform-origin: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script context="module">
|
||||||
|
import {writable} from "src/engine"
|
||||||
|
|
||||||
|
const maxWot = writable(10)
|
||||||
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import cx from "classnames"
|
import cx from "classnames"
|
||||||
|
import {themeColors} from "src/partials/state"
|
||||||
import Popover from "src/partials/Popover.svelte"
|
import Popover from "src/partials/Popover.svelte"
|
||||||
import Anchor from "src/partials/Anchor.svelte"
|
import Anchor from "src/partials/Anchor.svelte"
|
||||||
import {deriveFollowing, derivePerson, displayPerson, session, getWotScore} from "src/engine"
|
import {
|
||||||
|
deriveFollowing,
|
||||||
|
derivePerson,
|
||||||
|
displayPerson,
|
||||||
|
displayNpub,
|
||||||
|
session,
|
||||||
|
getWotScore,
|
||||||
|
} from "src/engine"
|
||||||
|
|
||||||
export let pubkey
|
export let pubkey
|
||||||
|
|
||||||
const person = derivePerson(pubkey)
|
const person = derivePerson(pubkey)
|
||||||
const following = deriveFollowing(pubkey)
|
const following = deriveFollowing(pubkey)
|
||||||
const wotScore = getWotScore($session?.pubkey, pubkey)
|
const wotScore = getWotScore($session?.pubkey, pubkey)
|
||||||
|
const npubDisplay = displayNpub(pubkey)
|
||||||
|
|
||||||
|
maxWot.update(x => Math.max(x, wotScore + 10))
|
||||||
|
|
||||||
|
$: dashOffset = 100 - (Math.max(0, wotScore) / $maxWot) * 100
|
||||||
|
$: style = `transform: rotate(${dashOffset * 1.8 - 4}deg)`
|
||||||
|
$: stroke = $themeColors[$following || pubkey === $session?.pubkey ? "accent" : "white"]
|
||||||
|
$: personDisplay = displayPerson($person)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class={cx("flex gap-1", $$props.class)}>
|
<div class={cx("flex gap-1", $$props.class)}>
|
||||||
<span class="cy-person-name overflow-hidden text-ellipsis">
|
<div class="flex flex-col overflow-hidden text-ellipsis">
|
||||||
{displayPerson($person)}
|
<span class="cy-person-name">{personDisplay}</span>
|
||||||
</span>
|
{#if personDisplay != npubDisplay}
|
||||||
|
<small class="text-xs">{npubDisplay}</small>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
{#if $session}
|
{#if $session}
|
||||||
<div class="flex gap-1 font-normal">
|
<div class="flex gap-1 font-normal">
|
||||||
<Popover triggerType="mouseenter">
|
<Popover triggerType="mouseenter">
|
||||||
<span slot="trigger" class="whitespace-nowrap px-2 py-1 text-xs">
|
<span
|
||||||
{#if $following}
|
slot="trigger"
|
||||||
<i class="fa fa-check-circle text-accent" />
|
class="relative flex h-10 w-10 items-center justify-center whitespace-nowrap px-4 text-xs">
|
||||||
{:else}
|
<svg height="32" width="32" class="absolute">
|
||||||
<i class="fa fa-diagram-project text-accent" />
|
<circle class="wot-background" cx="16" cy="16" r="15" />
|
||||||
{/if}
|
<circle
|
||||||
|
cx="16"
|
||||||
|
cy="16"
|
||||||
|
r="15"
|
||||||
|
class="wot-highlight"
|
||||||
|
stroke-dashoffset={dashOffset}
|
||||||
|
{style}
|
||||||
|
{stroke} />
|
||||||
|
</svg>
|
||||||
{wotScore}
|
{wotScore}
|
||||||
</span>
|
</span>
|
||||||
<Anchor modal slot="tooltip" class="flex items-center gap-1" href="/help/web-of-trust">
|
<Anchor modal slot="tooltip" class="flex items-center gap-1" href="/help/web-of-trust">
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {throttle} from "throttle-debounce"
|
import {throttle} from "throttle-debounce"
|
||||||
|
import {slide} from "src/util/transition"
|
||||||
import {fuzzy} from "src/util/misc"
|
import {fuzzy} from "src/util/misc"
|
||||||
import {parseAnything} from "src/util/nostr"
|
import {parseAnything} from "src/util/nostr"
|
||||||
import {router} from "src/app/router"
|
import {router} from "src/app/router"
|
||||||
import type {Person, Topic} from "src/engine"
|
import type {Person, Topic} from "src/engine"
|
||||||
import {topics, derived, searchPeople, loadPeople} from "src/engine"
|
import {topics, derived, searchPeople, createPeopleLoader} from "src/engine"
|
||||||
|
|
||||||
export let term
|
export let term
|
||||||
export let replace = false
|
export let replace = false
|
||||||
|
export let showLoading = false
|
||||||
|
|
||||||
const openTopic = topic => router.at("topics").of(topic).open({replace})
|
const openTopic = topic => router.at("topics").of(topic).open({replace})
|
||||||
|
|
||||||
@ -39,6 +41,8 @@
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const {loading: loadingPeople, load: loadPeople} = createPeopleLoader()
|
||||||
|
|
||||||
const searchTopics = topics
|
const searchTopics = topics
|
||||||
.throttle(1000)
|
.throttle(1000)
|
||||||
.derived($topics => fuzzy($topics, {keys: ["name"], threshold: 0.5, shouldSort: true}))
|
.derived($topics => fuzzy($topics, {keys: ["name"], threshold: 0.5, shouldSort: true}))
|
||||||
@ -72,3 +76,11 @@
|
|||||||
{:else}
|
{:else}
|
||||||
<p class="text-center py-12">No results found.</p>
|
<p class="text-center py-12">No results found.</p>
|
||||||
{/each}
|
{/each}
|
||||||
|
{#if showLoading && $loadingPeople}
|
||||||
|
<div transition:slide|local class="flex gap-2 bg-cocoa px-4 py-2 text-lighter absolute bottom-0 left-0 right-0">
|
||||||
|
<div>
|
||||||
|
<i class="fa fa-circle-notch fa-spin" />
|
||||||
|
</div>
|
||||||
|
Loading more options...
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
@ -11,7 +11,15 @@
|
|||||||
import PersonSummary from "src/app/shared/PersonSummary.svelte"
|
import PersonSummary from "src/app/shared/PersonSummary.svelte"
|
||||||
import RelayCard from "src/app/shared/RelayCard.svelte"
|
import RelayCard from "src/app/shared/RelayCard.svelte"
|
||||||
import type {Relay} from "src/engine"
|
import type {Relay} from "src/engine"
|
||||||
import {env, lists, urlToRelay, mention, loadPeople, searchPeople, searchRelays} from "src/engine"
|
import {
|
||||||
|
env,
|
||||||
|
lists,
|
||||||
|
urlToRelay,
|
||||||
|
mention,
|
||||||
|
createPeopleLoader,
|
||||||
|
searchPeople,
|
||||||
|
searchRelays,
|
||||||
|
} from "src/engine"
|
||||||
|
|
||||||
export let relays
|
export let relays
|
||||||
export let petnames
|
export let petnames
|
||||||
@ -24,6 +32,8 @@
|
|||||||
let showPersonSearch
|
let showPersonSearch
|
||||||
let showRelaySearch
|
let showRelaySearch
|
||||||
|
|
||||||
|
const {load: loadPeople} = createPeopleLoader()
|
||||||
|
|
||||||
const prev = () => setStage("profile")
|
const prev = () => setStage("profile")
|
||||||
const next = () => setStage("note")
|
const next = () => setStage("note")
|
||||||
|
|
||||||
@ -138,8 +148,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<Anchor button on:click={prev}><i class="fa fa-arrow-left" /> Back</Anchor>
|
<Anchor button on:click={prev}><i class="fa fa-arrow-left" /> Back</Anchor>
|
||||||
<Anchor button accent class="flex-grow" on:click={() => next()}
|
<Anchor button accent class="flex-grow" on:click={() => next()}>Continue</Anchor>
|
||||||
>Continue</Anchor>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if showList}
|
{#if showList}
|
||||||
|
@ -78,17 +78,19 @@
|
|||||||
<i slot="before" class="fa fa-search" />
|
<i slot="before" class="fa fa-search" />
|
||||||
<i slot="after" class="fa fa-qrcode cursor-pointer" on:click={startScanner} />
|
<i slot="after" class="fa fa-qrcode cursor-pointer" on:click={startScanner} />
|
||||||
</Input>
|
</Input>
|
||||||
<SearchResults replace term={searchTerm}>
|
<div class="relative max-h-full">
|
||||||
<div slot="result" let:result>
|
<SearchResults replace term={searchTerm}>
|
||||||
{#if result.type === "topic"}
|
<div slot="result" let:result>
|
||||||
<Card interactive>
|
{#if result.type === "topic"}
|
||||||
#{result.topic.name}
|
<Card interactive>
|
||||||
</Card>
|
#{result.topic.name}
|
||||||
{:else if result.type === "profile"}
|
</Card>
|
||||||
<Card interactive>
|
{:else if result.type === "profile"}
|
||||||
<PersonSummary inert hideActions pubkey={result.id} />
|
<Card interactive>
|
||||||
</Card>
|
<PersonSummary inert hideActions pubkey={result.id} />
|
||||||
{/if}
|
</Card>
|
||||||
</div>
|
{/if}
|
||||||
</SearchResults>
|
</div>
|
||||||
|
</SearchResults>
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -1,13 +1,41 @@
|
|||||||
import {debounce} from "throttle-debounce"
|
import {debounce} from "throttle-debounce"
|
||||||
|
import {always} from "ramda"
|
||||||
|
import {noop, sleep} from "hurdak"
|
||||||
|
import {writable} from "src/engine/core/utils"
|
||||||
import {load} from "src/engine/network/utils"
|
import {load} from "src/engine/network/utils"
|
||||||
import {searchableRelays} from "src/engine/relays/derived"
|
import {searchableRelays} from "src/engine/relays/derived"
|
||||||
|
import type {Event} from "src/engine/events/model"
|
||||||
|
|
||||||
export const loadPeople = debounce(500, search => {
|
type PeopleLoaderOpts = {
|
||||||
// Only search if we have a query
|
shouldLoad?: (term: string) => boolean
|
||||||
if (search.length > 2) {
|
onEvent?: (e: Event) => void
|
||||||
load({
|
}
|
||||||
relays: searchableRelays.get(),
|
|
||||||
filters: [{kinds: [0], search, limit: 100}],
|
export const createPeopleLoader = ({
|
||||||
})
|
shouldLoad = always(true),
|
||||||
|
onEvent = noop,
|
||||||
|
}: PeopleLoaderOpts = {}) => {
|
||||||
|
const loading = writable(false)
|
||||||
|
|
||||||
|
return {
|
||||||
|
loading,
|
||||||
|
load: debounce(500, term => {
|
||||||
|
if (term.length > 2 && shouldLoad(term)) {
|
||||||
|
const now = Date.now()
|
||||||
|
|
||||||
|
loading.set(true)
|
||||||
|
|
||||||
|
load({
|
||||||
|
onEvent,
|
||||||
|
relays: searchableRelays.get(),
|
||||||
|
filters: [{kinds: [0], search: term, limit: 100}],
|
||||||
|
onClose: async () => {
|
||||||
|
await sleep(Math.min(1000, Date.now() - now))
|
||||||
|
|
||||||
|
loading.set(false)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
@ -34,6 +34,12 @@ export const personHasName = ({profile: p}: Person) => Boolean(p?.name || p?.dis
|
|||||||
|
|
||||||
export const getPersonWithDefault = pubkey => ({pubkey, ...people.key(pubkey).get()})
|
export const getPersonWithDefault = pubkey => ({pubkey, ...people.key(pubkey).get()})
|
||||||
|
|
||||||
|
export const displayNpub = pubkey => {
|
||||||
|
const d = nip19.npubEncode(pubkey)
|
||||||
|
|
||||||
|
return d.slice(0, 8) + "..." + d.slice(-5)
|
||||||
|
}
|
||||||
|
|
||||||
export const displayPerson = ({pubkey, profile}: Person) => {
|
export const displayPerson = ({pubkey, profile}: Person) => {
|
||||||
if (profile) {
|
if (profile) {
|
||||||
const {display_name, name} = profile
|
const {display_name, name} = profile
|
||||||
@ -48,7 +54,7 @@ export const displayPerson = ({pubkey, profile}: Person) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return nip19.npubEncode(pubkey).slice(-8)
|
return displayNpub(pubkey)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
|
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
import {whereEq, when, reject, uniqBy, prop, inc} from "ramda"
|
import {whereEq, when, reject, uniqBy, prop, inc} from "ramda"
|
||||||
import {now, normalizeRelayUrl, isShareableRelay} from "paravel"
|
import {now, normalizeRelayUrl, isShareableRelay, createEvent} from "paravel"
|
||||||
import {people} from "src/engine/people/state"
|
import {people} from "src/engine/people/state"
|
||||||
import {canSign, stateKey} from "src/engine/session/derived"
|
import {canSign, signer, stateKey} from "src/engine/session/derived"
|
||||||
import {updateStore} from "src/engine/core/commands"
|
import {updateStore} from "src/engine/core/commands"
|
||||||
import {pool} from "src/engine/network/state"
|
import {createAndPublish, getClientTags, Publisher} from "src/engine/network/utils"
|
||||||
import {createAndPublish, getClientTags} from "src/engine/network/utils"
|
|
||||||
import type {RelayPolicy} from "./model"
|
import type {RelayPolicy} from "./model"
|
||||||
import {relays} from "./state"
|
import {relays} from "./state"
|
||||||
import {relayPolicies} from "./derived"
|
import {relayPolicies} from "./derived"
|
||||||
@ -58,16 +57,16 @@ export const publishRelays = ($relays: RelayPolicy[]) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const joinRelay = (url: string, claim?: string) => {
|
export const joinRelay = async (url: string, claim?: string) => {
|
||||||
// Fire off the claim to join the relay
|
// Fire off the claim to join the relay
|
||||||
if (claim) {
|
if (claim) {
|
||||||
Publisher.publish({
|
Publisher.publish({
|
||||||
relays: [url],
|
relays: [url],
|
||||||
event: createEvent(28934, {
|
event: await signer.get().signAsUser(
|
||||||
"tags": [
|
createEvent(28934, {
|
||||||
["claim", claim],
|
tags: [["claim", claim]],
|
||||||
],
|
})
|
||||||
})
|
),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,12 +23,11 @@
|
|||||||
className = cx({
|
className = cx({
|
||||||
"border-r-4 border-transparent": border,
|
"border-r-4 border-transparent": border,
|
||||||
"cursor-pointer transition-colors": interactive,
|
"cursor-pointer transition-colors": interactive,
|
||||||
"hover:border-cocoa": border && interactive && isAlt,
|
"hover:border-accent": border && interactive,
|
||||||
"hover:border-dark-l": border && interactive && !isAlt,
|
|
||||||
"bg-cocoa": isAlt,
|
"bg-cocoa": isAlt,
|
||||||
"hover:bg-cocoa-d": isAlt && interactive,
|
// "hover:bg-cocoa-d": isAlt && interactive && !border,
|
||||||
"bg-dark": !isAlt,
|
"bg-dark": !isAlt,
|
||||||
"hover:bg-dark-d": !isAlt && interactive,
|
// "hover:bg-dark-d": !isAlt && interactive && !border,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
@ -44,7 +44,7 @@
|
|||||||
<AlternatingBackground
|
<AlternatingBackground
|
||||||
border
|
border
|
||||||
{interactive}
|
{interactive}
|
||||||
class={cx($$props.class, "rounded p-3 text-lightest")}>
|
class={cx($$props.class, "rounded py-5 px-7 text-lightest")}>
|
||||||
<slot />
|
<slot />
|
||||||
</AlternatingBackground>
|
</AlternatingBackground>
|
||||||
</div>
|
</div>
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
export let displayItem = getKey
|
export let displayItem = getKey
|
||||||
export let autofocus = false
|
export let autofocus = false
|
||||||
export let multiple = false
|
export let multiple = false
|
||||||
|
export let loading = false
|
||||||
export let defaultOptions = []
|
export let defaultOptions = []
|
||||||
export let term = multiple ? "" : displayItem(value)
|
export let term = multiple ? "" : displayItem(value)
|
||||||
|
|
||||||
@ -134,6 +135,7 @@
|
|||||||
<Suggestions
|
<Suggestions
|
||||||
bind:this={suggestions}
|
bind:this={suggestions}
|
||||||
create={termToItem ? create : null}
|
create={termToItem ? create : null}
|
||||||
|
{loading}
|
||||||
{select}
|
{select}
|
||||||
{term}
|
{term}
|
||||||
{getKey}>
|
{getKey}>
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {identity} from "ramda"
|
import {identity} from "ramda"
|
||||||
|
import {slide} from "src/util/transition"
|
||||||
|
|
||||||
export let select
|
export let select
|
||||||
export let term = null
|
export let term = null
|
||||||
export let create = null
|
export let create = null
|
||||||
|
export let loading = false
|
||||||
export let getKey = identity
|
export let getKey = identity
|
||||||
|
|
||||||
let data = []
|
let data = []
|
||||||
@ -37,7 +39,7 @@
|
|||||||
|
|
||||||
{#if data.length > 0 || (create && term)}
|
{#if data.length > 0 || (create && term)}
|
||||||
<div
|
<div
|
||||||
class="mt-2 flex max-h-[350px] flex-col overflow-y-auto overflow-x-hidden rounded border border-solid border-mid">
|
class="mt-2 flex max-h-[350px] flex-col overflow-y-auto overflow-x-hidden border border-solid border-mid">
|
||||||
{#if create && term}
|
{#if create && term}
|
||||||
{@const i = data.length}
|
{@const i = data.length}
|
||||||
<button
|
<button
|
||||||
@ -65,3 +67,11 @@
|
|||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
{#if loading}
|
||||||
|
<div transition:slide|local class="flex gap-2 bg-cocoa px-4 py-2 text-lighter">
|
||||||
|
<div>
|
||||||
|
<i class="fa fa-circle-notch fa-spin" />
|
||||||
|
</div>
|
||||||
|
Loading more options...
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
Loading…
Reference in New Issue
Block a user