Skip to content

Commit

Permalink
feat: toggle dropdown on unfocus and click
Browse files Browse the repository at this point in the history
  • Loading branch information
ItsNotGoodName committed Mar 12, 2023
1 parent 5825aee commit d074381
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 54 deletions.
5 changes: 4 additions & 1 deletion http/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ func Start(a API, port int, fs fs.FS) {
e.HideBanner = true
e.HidePort = true

e.Use(middleware.Logger())
e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
Format: "[${time_rfc3339}] ${status} ${method} ${path} (${remote_ip}) ${latency_human}\n",
Output: e.Logger.Output(),
}))
e.Use(middleware.Recover())
e.Use(middleware.CORS())

Expand Down
24 changes: 23 additions & 1 deletion web/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
104 changes: 66 additions & 38 deletions web/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import {
createEffect,
on,
Show,
type ParentComponent,
type JSX,
splitProps,
batch,
Expand All @@ -36,7 +35,7 @@ import {
useRefreshRadioVolume,
useRefreshRadioSubscription,
} from "./store";
import { clickOutside, type ClassProps, mergeClass } from "./utils";
import { type ClassProps, mergeClass, useDropdown, IOS } from "./utils";
import { useWS } from "./ws";
import {
DaisyButton,
Expand All @@ -46,9 +45,6 @@ import {
DaisyDropdownButton,
} from "./Daisy";

// Prevent TypeScript from removing clickOutside directive
false && clickOutside;

const DiscoverButton: Component<
{ discovering: boolean; classButton?: string } & ClassProps
> = (props) => {
Expand Down Expand Up @@ -186,17 +182,20 @@ const RadioPlayerTitleDropdown: Component<
),
},
];
const { showDropdown, toggleDropdown } = useDropdown();

return (
<div
class={mergeClass("dropdown no-animation", props.class)}
use:clickOutside=""
classList={{ "dropdown-show": showDropdown() }}
onFocusOut={toggleDropdown}
>
<DaisyDropdownButton
class={mergeClass(
"btn-primary justify-start gap-2 truncate",
props.classButton
)}
onClick={toggleDropdown}
loading={props.loading}
>
<>
Expand All @@ -209,7 +208,7 @@ const RadioPlayerTitleDropdown: Component<
<div
tabindex="0"
class={mergeClass(
"card-compact card dropdown-content w-full bg-primary p-2 text-primary-content shadow",
"card dropdown-content card-compact w-full bg-primary p-2 text-primary-content shadow",
props.classDropdown
)}
>
Expand All @@ -232,15 +231,24 @@ const RadioTypeDropdown: Component<
{ key: "Model Number", value: props.state.model_number },
];

const { showDropdown, toggleDropdown } = useDropdown();

return (
<div class={mergeClass("dropdown", props.class)} use:clickOutside="">
<DaisyDropdownButton class={mergeClass("btn-primary", props.classButton)}>
<div
class={mergeClass("dropdown", props.class)}
classList={{ "dropdown-show": showDropdown() }}
onFocusOut={toggleDropdown}
>
<DaisyDropdownButton
class={mergeClass("btn-primary", props.classButton)}
onClick={toggleDropdown}
>
<FaSolidRadio size={20} />
</DaisyDropdownButton>
<div
tabindex="0"
class={mergeClass(
"card-compact card dropdown-content w-80 bg-primary p-2 text-primary-content shadow",
"card dropdown-content card-compact w-80 bg-primary p-2 text-primary-content shadow",
props.classDropdown
)}
>
Expand Down Expand Up @@ -441,12 +449,19 @@ const RadioAudioSourceDropdown: Component<
});
};

const { showDropdown, toggleDropdown } = useDropdown();

return (
<div class={mergeClass("dropdown", props.class)}>
<div
class={mergeClass("dropdown", props.class)}
classList={{ "dropdown-open": showDropdown() }}
onFocusOut={toggleDropdown}
>
<DaisyDropdownButton
class={props.classButton}
classList={{ "btn-secondary": !!props.state.audio_source }}
aria-label="Audio Source"
onclick={toggleDropdown}
>
<FaBrandsItunesNote size={20} />
</DaisyDropdownButton>
Expand Down Expand Up @@ -543,10 +558,10 @@ const App: Component = () => {
loading={loading()}
/>
</div>
<div class="container mx-auto py-20 px-4">
<div class="container mx-auto px-4 pt-20 pb-36">
<RadioPresetsList radioUUID={radioUUID} state={state} />
</div>
<div class="fixed bottom-0 z-50 w-full space-y-2">
<div class="fixed bottom-0 z-50 w-full space-y-2 ">
<div class="ml-auto max-w-screen-sm space-y-2 px-2">
<Show when={!!radiosListQuery[0].error}>
<div class="alert alert-error shadow-lg">
Expand All @@ -570,33 +585,46 @@ const App: Component = () => {
</div>
</Show>
</div>
<div class="flex gap-2 border-t-2 border-accent border-t-base-300 bg-base-200 p-2">
<div class="flex flex-1">
<DiscoverButton
classButton="rounded-r-none"
discovering={discovering()}
/>
<RadioSelect
class="w-full flex-1 rounded-l-none"
radioUUID={radioUUID}
setRadioUUID={setRadioUUID}
/>
<div
class="flex flex-wrap-reverse gap-2 border-t-2 border-accent border-t-base-300 bg-base-200 p-2"
classList={{ "pb-5": IOS() }}
>
<div class="flex flex-auto gap-2">
<div class="flex flex-1">
<DiscoverButton
classButton="rounded-r-none"
discovering={discovering()}
/>
<RadioSelect
class="w-full min-w-fit flex-1 rounded-l-none"
radioUUID={radioUUID}
setRadioUUID={setRadioUUID}
/>
</div>
<Show when={radioSelected()}>
<RadioRefreshSubscriptionButton radioUUID={radioUUID} />
</Show>
</div>
<Show when={radioSelected()}>
<RadioRefreshSubscriptionButton radioUUID={radioUUID} />
<RadioPowerButton radioUUID={radioUUID} state={state} />
<RadioVolumeButtonGroup radioUUID={radioUUID} state={state} />
<RadioAudioSourceDropdown
class="dropdown-top dropdown-end"
classDropdown="mb-2"
radioUUID={radioUUID}
state={state}
/>
<RadioTypeDropdown
class="dropdown-top dropdown-end"
classDropdown="mb-2"
state={state}
/>
<div class="flex flex-auto gap-2">
<RadioPowerButton
class="flex-auto"
radioUUID={radioUUID}
state={state}
/>
<RadioVolumeButtonGroup radioUUID={radioUUID} state={state} />
<RadioAudioSourceDropdown
class="dropdown-top dropdown-end"
classDropdown="mb-2"
radioUUID={radioUUID}
state={state}
/>
<RadioTypeDropdown
class="dropdown-top dropdown-end"
classDropdown="mb-2"
state={state}
/>
</div>
</Show>
</div>
</div>
Expand Down
48 changes: 34 additions & 14 deletions web/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
createEffect,
on,
type Signal,
onCleanup,
} from "solid-js";

export function createStaleSignal<T>(value: T): Signal<T> {
Expand Down Expand Up @@ -51,21 +50,42 @@ export function mergeClass(first: string, second?: string) {
return first;
}

export function clickOutside(el: HTMLInputElement) {
const onClick = (e: MouseEvent) => {
!el.contains(e.target as Node) && el.blur();
export function useDropdown() {
const [show, setShow] = createSignal(false);
const toggleShow = (
e: Event & {
currentTarget: HTMLElement;
target: Element;
}
) => {
if (e.type == "focusout") {
setShow(false);
return;
}

if (!show()) {
setShow(true);
} else {
setShow(false);
e.currentTarget.blur();
}
};
document.body.addEventListener("click", onClick);

onCleanup(() => document.body.removeEventListener("click", onClick));
return { showDropdown: show, toggleDropdown: toggleShow };
}

declare module "solid-js" {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace JSX {
interface Directives {
// use:clickOutside
clickOutside: string;
}
}
// https://stackoverflow.com/a/9039885
export function IOS() {
return (
[
"iPad Simulator",
"iPhone Simulator",
"iPod Simulator",
"iPad",
"iPhone",
"iPod",
].includes(navigator.platform) ||
// iPad on iOS 13 detection
(navigator.userAgent.includes("Mac") && "ontouchend" in document)
);
}

0 comments on commit d074381

Please sign in to comment.