Skip to content

Commit

Permalink
add entry list arrow navigation (#29)
Browse files Browse the repository at this point in the history
* add arrow nav to entry list

* wip
  • Loading branch information
zaknesler authored May 19, 2024
1 parent f762083 commit efa372c
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 36 deletions.
1 change: 1 addition & 0 deletions ui/src/components/entry/entry-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export const EntryItem: Component<EntryItemProps> = props => {

return (
<A
data-entry-item-uuid={props.entry.uuid}
href={filter.getEntryUrl(props.entry.uuid)}
activeClass="bg-gray-100 dark:bg-gray-950"
inactiveClass={cx(
Expand Down
27 changes: 20 additions & 7 deletions ui/src/components/entry/entry-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { useFeeds } from '~/hooks/queries/use-feeds';
import { Empty } from '../ui/empty';
import { useFilterParams } from '~/hooks/use-filter-params';
import { getEntryComparator } from '~/utils/entries';
import { useListNav } from '~/hooks/use-list-nav';

type EntryListProps = {
containerBounds?: Readonly<NullableBounds>;
Expand All @@ -28,8 +29,9 @@ export const EntryList: Component<EntryListProps> = props => {
const [localFeedKey, setLocalFeedKey] = createSignal(filter.getFeedUrl());

createEffect(() => {
const bottomOfListVisible =
props.containerBounds?.bottom && listBounds.bottom && listBounds.bottom <= props.containerBounds?.bottom;
if (!listBounds.bottom || !props.containerBounds?.bottom) return;

const bottomOfListVisible = listBounds.bottom <= props.containerBounds.bottom;
if (!bottomOfListVisible) return;

entries.fetchMore();
Expand All @@ -46,6 +48,21 @@ export const EntryList: Component<EntryListProps> = props => {
setLocalEntries([...localEntries(), ...newEntries].sort(getEntryComparator(filter.getSort())));
});

createEffect(() => {
if (!filter.params.entry_uuid || !props.containerBounds?.bottom) return;

const activeItem = document.querySelector(`[data-entry-item-uuid="${filter.params.entry_uuid}"]`);
if (!(activeItem instanceof HTMLElement)) return;

const bounds = activeItem.getBoundingClientRect();
const containerBottom = props.containerBounds.bottom;

const nearBounds = bounds.top <= containerBottom * 0.25 || bounds.bottom >= containerBottom * 0.9;
if (!nearBounds) return;

activeItem.scrollIntoView({ block: 'center' });
});

createEffect(() => {
const feedKey = filter.getFeedUrl();

Expand All @@ -57,11 +74,7 @@ export const EntryList: Component<EntryListProps> = props => {
setLocalEntries(entries.getAllEntries());
});

// useListNav(() => ({
// entries: entries.data || [],
// current_entry_uuid: props.current_entry_uuid,
// getUrl,
// }));
useListNav(() => ({ entries: localEntries() }));

return (
<Switch>
Expand Down
4 changes: 2 additions & 2 deletions ui/src/components/entry/entry-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { Spinner } from '../ui/spinner';
export const EntryPanel = () => {
const filter = useFilterParams();
const queryClient = useQueryClient();
const { aboveBreakpoint } = useViewport();
const { gtBreakpoint } = useViewport();

const entry = createQuery(() => ({
enabled: !!filter.params.entry_uuid,
Expand Down Expand Up @@ -42,7 +42,7 @@ export const EntryPanel = () => {
<Show
when={filter.params.entry_uuid}
fallback={
aboveBreakpoint('md') && (
gtBreakpoint('md') && (
<Panel class="h-full w-full p-4 lg:p-8">
<Empty />
</Panel>
Expand Down
36 changes: 18 additions & 18 deletions ui/src/hooks/use-list-nav.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,44 +2,44 @@ import { useNavigate } from '@solidjs/router';
import { createEffect } from 'solid-js';
import type { Entry } from '~/types/bindings';
import { useKeyDownEvent } from '@solid-primitives/keyboard';
import { useFilterParams } from './use-filter-params';
import { debounce } from '@solid-primitives/scheduled';
import { useViewport } from './use-viewport';

type UseListNavParams = {
entries: Entry[];
current_entry_uuid?: string;
getUrl: (uuid: string) => string;
};

export const useListNav = (params: () => UseListNavParams) => {
const filter = useFilterParams();
const keyDownEvent = useKeyDownEvent();
const navigate = useNavigate();
const viewport = useViewport();

createEffect(() => {
if (!params().entries.length) return;

const e = keyDownEvent();
if (!e) return;
const maybeNavigate = debounce((direction: 'up' | 'down') => {
const currentIndex = params().entries.findIndex(entry => entry.uuid === filter.params.entry_uuid);

const maybeNavigate = (direction: 'up' | 'down') => {
e.preventDefault();
const offset = direction === 'up' ? -1 : 1;
const entry = params().entries[currentIndex + offset];
if (!entry) return;

const currentIndex = params().entries.findIndex(entry => entry.uuid === params().current_entry_uuid);
if (currentIndex === -1) return;
navigate(filter.getEntryUrl(entry.uuid));
}, 30);

const offset = direction === 'up' ? -1 : 1;
const entry = params().entries[currentIndex + offset];
if (!entry) return;

console.log('navigate to', entry.uuid);
createEffect(() => {
if (!params().entries.length || viewport.lteBreakpoint('md')) return;

navigate(params().getUrl(entry.uuid));
};
const e = keyDownEvent();
if (!e) return;

switch (e.key) {
case 'ArrowDown':
e.preventDefault();
maybeNavigate('down');
break;

case 'ArrowUp':
e.preventDefault();
maybeNavigate('up');
break;
}
Expand Down
12 changes: 6 additions & 6 deletions ui/src/hooks/use-viewport.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { screens } from '~/constants/screens';
import { createWindowSize } from '@solid-primitives/resize-observer';
import { useWindowSize } from '@solid-primitives/resize-observer';

export type ScreenSize = keyof typeof screens;
export const getBreakpoint = (screen: ScreenSize) => +screens[screen].replace('px', '');

export const useViewport = () => {
const size = createWindowSize();
const size = useWindowSize();

const belowBreakpoint = (screen: ScreenSize) => size.width <= getBreakpoint(screen);
const aboveBreakpoint = (screen: ScreenSize) => size.width > getBreakpoint(screen);
const lteBreakpoint = (screen: ScreenSize) => size.width <= getBreakpoint(screen);
const gtBreakpoint = (screen: ScreenSize) => size.width > getBreakpoint(screen);

return {
size,
belowBreakpoint,
aboveBreakpoint,
lteBreakpoint,
gtBreakpoint,
};
};
6 changes: 3 additions & 3 deletions ui/src/routes/feed.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { MenuFeeds } from '~/components/menus/menu-feeds';
export default () => {
const filter = useFilterParams();
const isRouting = useIsRouting();
const { belowBreakpoint } = useViewport();
const { lteBreakpoint } = useViewport();

const [_showFeeds, setShowFeeds] = createSignal(false);
const [allFeedsMenuOpen, setAllFeedsMenuOpen] = createSignal(false);
Expand All @@ -41,9 +41,9 @@ export default () => {

const viewingEntry = () => !!filter.params.entry_uuid;

const isMobile = () => belowBreakpoint('md');
const isMobile = () => lteBreakpoint('md');
const showPanel = () => !isMobile() || (isMobile() && !viewingEntry());
const showFeeds = () => belowBreakpoint('xl') && _showFeeds();
const showFeeds = () => lteBreakpoint('xl') && _showFeeds();

return (
<>
Expand Down

0 comments on commit efa372c

Please sign in to comment.