wip tag-editor

This commit is contained in:
Paul Miller
2023-04-24 13:11:14 -05:00
parent c1296a1295
commit 339904737b
8 changed files with 181 additions and 25 deletions

View File

@@ -38,6 +38,7 @@
"@nostr-dev-kit/ndk": "^0.0.13",
"@solidjs/meta": "^0.28.4",
"@solidjs/router": "^0.8.2",
"@thisbeyond/solid-select": "^0.14.0",
"class-variance-authority": "^0.4.0",
"nostr-tools": "^1.10.1",
"qr-scanner": "^1.4.2",

11
pnpm-lock.yaml generated
View File

@@ -28,6 +28,9 @@ dependencies:
'@solidjs/router':
specifier: ^0.8.2
version: 0.8.2(solid-js@1.7.3)
'@thisbeyond/solid-select':
specifier: ^0.14.0
version: 0.14.0(solid-js@1.7.3)
class-variance-authority:
specifier: ^0.4.0
version: 0.4.0(typescript@4.9.5)
@@ -1971,6 +1974,14 @@ packages:
tslib: 2.5.0
dev: false
/@thisbeyond/solid-select@0.14.0(solid-js@1.7.3):
resolution: {integrity: sha512-ecq4U3Vnc/nJbU84ARuPg2scNuYt994ljF5AmBlzuZW87x43mWiGJ5hEWufIJJMpDT6CcnCIx/xbrdDkaDEHQw==}
peerDependencies:
solid-js: ^1.5
dependencies:
solid-js: 1.7.3
dev: false
/@types/babel__core@7.20.0:
resolution: {integrity: sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==}
dependencies:

View File

@@ -0,0 +1,84 @@
import { Select, createOptions } from "@thisbeyond/solid-select";
import { For, createSignal } from "solid-js";
import "~/styles/solid-select.css"
import { SmallHeader } from "./layout";
// take two arrays, subtract the second from the first, then return the first
function subtract<T>(a: T[], b: T[]) {
const set = new Set(b);
return a.filter(x => !set.has(x));
};
// combine two arrays and keep only the items that are unique
function combineUnique<T>(a: T[], b: T[]) {
const set = new Set([...a, ...b]);
return [...set];
};
// simple math.random based id generator
const createUniqueId = () => Math.random().toString(36).substr(2, 9);
type Contact = {
id: string;
name: string;
}
const fakeContacts: Contact[] = [
{ id: createUniqueId(), name: "👤 Alice" },
{ id: createUniqueId(), name: "👤 Tony" },
{ id: createUniqueId(), name: "👤 @benthecarman" },
{ id: createUniqueId(), name: "👀 Uknown" }
]
const createValue = (name: string) => {
return { id: createUniqueId(), name };
};
export function TagEditor() {
const candidates = [...fakeContacts];
const [values, setValues] = createSignal(candidates);
const [selectedValues, setSelectedValues] = createSignal<Contact[]>([]);
const onChange = (selected: Contact[]) => {
setSelectedValues(selected);
const lastValue = selected[selected.length - 1];
if (lastValue && !values().includes(lastValue)) {
setValues([...values(), lastValue]);
}
};
const props = createOptions(values, {
key: "name",
disable: (value) => selectedValues().includes(value),
filterable: true, // Default
createable: createValue,
});
return (
<div class="flex flex-col gap-2 flex-grow flex-shrink flex-1" >
<SmallHeader>Tag the origin</SmallHeader>
<Select
multiple
onChange={onChange}
placeholder="Where's it coming from?"
{...props}
/>
<div class="flex gap-2 flex-wrap">
<For each={subtract(fakeContacts, selectedValues())}>
{(contact) => (
<div onClick={() => onChange([...selectedValues(), contact])} class="bg-m-blue px-1 rounded cursor-pointer hover:outline-white hover:outline-1">+ {contact.name}</div>
)}
</For>
</div>
{/* <div>
<pre>{JSON.stringify(selectedValues(), null, 2)}</pre>
</div>
<div>
<pre>{JSON.stringify(values(), null, 2)}</pre>
</div> */}
</div >
)
}

View File

View File

@@ -2,5 +2,5 @@ import { A } from "solid-start";
import { Back } from "~/assets/svg/Back";
export function BackButton(props: { href?: string, title?: string }) {
return (<A href={props.href ? props.href : "/"} class="text-m-red text-xl font-semibold no-underline md:hidden flex items-center"><Back />{props.title ? props.title : "Home"}</A>)
return (<A href={props.href ? props.href : "/"} class="text-m-red active:text-m-red/80 text-xl font-semibold no-underline md:hidden flex items-center"><Back />{props.title ? props.title : "Home"}</A>)
}

View File

@@ -40,11 +40,10 @@ const FancyCard: ParentComponent<{ title?: string, tag?: JSX.Element }> = (props
const SafeArea: ParentComponent = (props) => {
return (
<div class="safe-top safe-left safe-right safe-bottom flex flex-col h-screen-safe">
<div class="flex-1 disable-scrollbars overflow-y-scroll md:pl-[8rem] md:pr-[6rem]">
<div class="safe-top safe-left safe-right safe-bottom">
{/* <div class="flex-1 disable-scrollbars overflow-y-scroll md:pl-[8rem] md:pr-[6rem]"> */}
{props.children}
<div class="h-32" />
</div>
{/* </div> */}
</div >
)
}

View File

@@ -15,6 +15,7 @@ import party from '~/assets/party.gif';
import { Amount } from "~/components/Amount";
import { FullscreenModal } from "~/components/layout/FullscreenModal";
import { BackButton } from "~/components/layout/BackButton";
import { TagEditor } from "~/components/TagEditor";
type OnChainTx = {
transaction: {
@@ -181,28 +182,21 @@ export default function Receive() {
return (
<NodeManagerGuard>
<SafeArea>
<DefaultMain>
<main class="max-w-[600px] flex flex-col gap-4 mx-auto p-4">
<BackButton />
<LargeHeader>Receive Bitcoin</LargeHeader>
<Switch>
<Match when={!unified() || receiveState() === "edit"}>
<dl>
<dd>
<AmountEditable initialAmountSats={amount() || "0"} setAmountSats={setAmount} onSave={handleAmountSave} />
<div>
<Button intent="glowy" layout="xs">Tag the sender</Button>
</div>
<form class="flex flex-col gap-4" onSubmit={onSubmit} >
<TextField.Root
value={label()}
onValueChange={setLabel}
class="flex flex-col gap-2"
>
<TextField.Label><SmallHeader>Label (private)</SmallHeader></TextField.Label>
<TextField.Input
ref={el => labelInput = el}
class="w-full p-2 rounded-lg text-black" />
</TextField.Root>
<Button disabled={!amount() || !label()} intent="green" type="submit">Create Invoice</Button>
</form >
</dd>
<dd>
<TagEditor />
</dd>
</dl>
<Button class="w-full" disabled={!amount() || !label()} intent="green" onClick={onSubmit}>Create Invoice</Button>
</Match>
<Match when={unified() && receiveState() === "show"}>
<div class="w-full bg-white rounded-xl">
@@ -247,7 +241,7 @@ export default function Receive() {
</FullscreenModal>
</Match>
</Switch>
</DefaultMain>
</main>
<NavBar activeTab="receive" />
</SafeArea >
</NodeManagerGuard>

View File

@@ -0,0 +1,67 @@
.solid-select-container[data-disabled="true"] {
@apply pointer-events-none;
}
.solid-select-container {
@apply relative;
}
.solid-select-control[data-disabled="true"] {
}
.solid-select-control {
@apply w-full p-2 rounded-lg bg-white/10 placeholder-neutral-400;
@apply grid leading-6;
grid-template-columns: repeat(1, minmax(0, 1fr));
}
.solid-select-control[data-multiple="true"][data-has-value="true"] {
@apply flex items-stretch gap-1 flex-wrap;
}
.solid-select-placeholder {
@apply text-neutral-400;
@apply col-start-1 row-start-1;
}
.solid-select-single-value {
@apply col-start-1 row-start-1;
}
.solid-select-multi-value {
@apply flex bg-white/20 rounded items-center px-1;
}
.solid-select-multi-value-remove {
/* TODO: there's gotta be a better way to vertically center this */
@apply pl-2 pr-1 leading-3 -mt-2 text-2xl;
}
.solid-select-input {
@apply bg-transparent caret-transparent flex-grow flex-shrink;
outline: 2px solid transparent;
@apply col-start-1 row-start-1;
}
.solid-select-input:read-only {
@apply cursor-default;
}
.solid-select-input[data-multiple="true"] {
@apply caret-current;
}
.solid-select-input[data-is-active="true"] {
@apply caret-current;
}
.solid-select-list {
@apply max-h-[50vh] min-w-full overflow-y-auto absolute whitespace-nowrap z-10 bg-neutral-950 p-2 rounded-lg;
}
.solid-select-option[data-focused="true"] {
}
.solid-select-option > mark {
@apply underline;
}
.solid-select-option {
@apply cursor-default select-none p-1 hover:bg-neutral-800 rounded;
}
.solid-select-option[data-disabled="true"] {
@apply pointer-events-none text-neutral-500;
}
.solid-select-list-placeholder {
@apply cursor-default select-none;
}