Skip to content

Commit

Permalink
[gn] improve add ux
Browse files Browse the repository at this point in the history
  • Loading branch information
a-type committed Dec 2, 2024
1 parent eadce66 commit 132b936
Show file tree
Hide file tree
Showing 25 changed files with 683 additions and 427 deletions.
2 changes: 1 addition & 1 deletion apps/gnocchi/hub/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"typecheck": "tsc --build tsconfig.json"
},
"dependencies": {
"@a-type/ui": "^1.2.0",
"@a-type/ui": "^1.2.1",
"@a-type/utils": "^1.0.8",
"@tiptap/core": "^2.2.4",
"@tiptap/extension-document": "^2.2.4",
Expand Down
2 changes: 1 addition & 1 deletion apps/gnocchi/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"typecheck": "tsc --build tsconfig.json"
},
"dependencies": {
"@a-type/ui": "^1.2.0",
"@a-type/ui": "^1.2.1",
"@a-type/utils": "^1.0.8",
"@biscuits/client": "workspace:*",
"@biscuits/error": "workspace:*",
Expand Down
2 changes: 1 addition & 1 deletion apps/gnocchi/web/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export function App() {
)}
>
<ErrorBoundary fallback={<ErrorFallback />}>
<UIProvider disableViewportOffset>
<UIProvider>
<Suspense fallback={<GlobalLoader />}>
<Provider
appId="gnocchi"
Expand Down
58 changes: 25 additions & 33 deletions apps/gnocchi/web/src/components/addBar/AddBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,25 @@ import classNames from 'classnames';
import { Suspense, forwardRef, useRef, useState } from 'react';
import { AddInput } from './AddInput.jsx';
import { SuggestionGroup } from './SuggestionGroup.jsx';
import { useAddBarCombobox, useAddBarSuggestions } from './hooks.js';
import {
useAddBarCombobox,
useAddBarSuggestions,
useKeepOpenAfterSelect,
} from './hooks.js';

export interface AddBarProps {
className?: string;
onAdd: (text: string[]) => Promise<void> | void;
showRichSuggestions?: boolean;
open?: boolean;
onOpenChange?: (open: boolean) => void;
}

export const AddBarImpl = forwardRef<HTMLDivElement, AddBarProps>(
function AddBarImpl(
{
onAdd,
showRichSuggestions = false,
open,
onOpenChange,
className,
...rest
},
{ onAdd, showRichSuggestions = false, className, ...rest },
ref,
) {
const [keepOpenOnSelect] = useKeepOpenAfterSelect();
const [open, onOpenChange] = useState(false);
const [suggestionPrompt, setSuggestionPrompt] = useState('');

const {
Expand All @@ -57,24 +54,22 @@ export const AddBarImpl = forwardRef<HTMLDivElement, AddBarProps>(
});

const {
combobox: {
isOpen,
getMenuProps,
getInputProps,
highlightedIndex,
getItemProps,
inputValue,
setInputValue,
selectItem,
openMenu,
},
addingRecipe,
clearAddingRecipe,
onInputPaste,
getMenuProps,
getInputProps,
getItemProps,
getSubmitButtonProps,
clear,
} = useAddBarCombobox({
setSuggestionPrompt,
allSuggestions,
onAdd,
onAdd: (items: string[], focusInput: boolean) => {
onAdd(items);
if (!keepOpenOnSelect) {
onOpenChange(false);
}
},
onOpenChange,
open,
});
Expand All @@ -85,17 +80,17 @@ export const AddBarImpl = forwardRef<HTMLDivElement, AddBarProps>(

return (
<>
<Popover open={isOpen}>
<Popover open={open}>
<PopoverAnchor asChild>
<AddInput
inputProps={getInputProps({
onPaste: onInputPaste,
placeholder,
onFocus: () => onOpenChange?.(true),
})}
isOpen={isOpen}
getSubmitButtonProps={getSubmitButtonProps}
isOpen={open}
className={className}
selectItem={selectItem}
clear={() => setInputValue('')}
clear={() => clear()}
ref={mergedRef}
{...rest}
/>
Expand Down Expand Up @@ -126,7 +121,6 @@ export const AddBarImpl = forwardRef<HTMLDivElement, AddBarProps>(
<SuggestionGroup
title="Suggested"
suggestions={mainSuggestions}
highlightedIndex={highlightedIndex}
getItemProps={getItemProps}
/>
)}
Expand All @@ -135,14 +129,12 @@ export const AddBarImpl = forwardRef<HTMLDivElement, AddBarProps>(
title="Expiring Soon"
suggestions={expiresSoonSuggestions}
getItemProps={getItemProps}
highlightedIndex={highlightedIndex}
/>
)}
{!noSuggestions && (
<SuggestionGroup
title={inputValue ? 'Matches' : 'Favorites'}
title={suggestionPrompt ? 'Matches' : 'Favorites'}
suggestions={matchSuggestions}
highlightedIndex={highlightedIndex}
getItemProps={getItemProps}
/>
)}
Expand Down
80 changes: 58 additions & 22 deletions apps/gnocchi/web/src/components/addBar/AddInput.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,37 @@
import { Button, Input, InputProps } from '@a-type/ui';
import {
Button,
ButtonProps,
Input,
InputProps,
useParticles,
} from '@a-type/ui';
import { isUrl } from '@a-type/utils';
import classNames from 'classnames';
import { forwardRef } from 'react';
import { forwardRef, useEffect, useRef } from 'react';
import { useSnapshot } from 'valtio';
import { groceriesState } from '../groceries/state.js';
import { Icon } from '../icons/Icon.jsx';
import { SuggestionData } from './hooks.js';

export interface AddInputProps {
inputProps: InputProps;
isOpen: boolean;
className?: string;
selectItem: (item: SuggestionData) => void;
clear: () => void;
disableInteraction?: boolean;
getSubmitButtonProps: () => any;
onFocus?: () => void;
}

export const AddInput = forwardRef<HTMLDivElement, AddInputProps>(
function AddInput(
{
inputProps,
getSubmitButtonProps,
isOpen,
className,
selectItem,
clear,
disableInteraction,
onFocus,
...rest
},
ref,
Expand Down Expand Up @@ -50,23 +59,7 @@ export const AddInput = forwardRef<HTMLDivElement, AddInputProps>(
{...inputProps}
/>
<div className="absolute flex flex-row-reverse gap-1 right-0px top-0px">
<Button
data-test="grocery-list-add-button"
color="primary"
size="icon"
className="md:(w-35px h-35px p-0) items-center justify-center"
onClick={() =>
selectItem({
type: 'raw',
text: inputValue,
id: inputValue,
})
}
aria-label={inputIsUrl ? 'scan recipe page' : 'add item'}
tabIndex={disableInteraction ? -1 : 0}
>
{inputIsUrl ? <Icon name="scan" /> : <Icon name="plus" />}
</Button>
<SubmitButton {...getSubmitButtonProps()} />
{!!inputValue && (
<Button
size="icon"
Expand All @@ -83,3 +76,46 @@ export const AddInput = forwardRef<HTMLDivElement, AddInputProps>(
);
},
);

function SubmitButton({
children,
inputIsUrl,
disableInteraction,
...rest
}: ButtonProps & {
inputIsUrl: boolean;
disableInteraction: boolean;
}) {
const { justAddedSomething } = useSnapshot(groceriesState);

const ref = useRef<HTMLButtonElement>(null);

const particles = useParticles();
useEffect(() => {
if (justAddedSomething) {
if (ref.current) {
particles?.addParticles(
particles.elementExplosion({
element: ref.current,
count: 20,
}),
);
}
}
}, [justAddedSomething]);

return (
<Button
data-test="grocery-list-add-button"
color="primary"
size="icon"
className="md:(w-35px h-35px p-0) items-center justify-center relative z-2"
aria-label={inputIsUrl ? 'scan recipe page' : 'add item'}
tabIndex={disableInteraction ? -1 : 0}
{...rest}
ref={ref}
>
{inputIsUrl ? <Icon name="scan" /> : <Icon name="plus" />}
</Button>
);
}
61 changes: 33 additions & 28 deletions apps/gnocchi/web/src/components/addBar/AddPane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ import { useAddBarCombobox, useAddBarSuggestions } from './hooks.js';

const AddPaneImpl = forwardRef<
HTMLDivElement,
AddBarProps & { disabled?: boolean }
AddBarProps & {
disabled?: boolean;
open?: boolean;
onOpenChange: (open: boolean) => void;
}
>(function AddPaneImpl(
{
onAdd,
Expand Down Expand Up @@ -48,29 +52,24 @@ const AddPaneImpl = forwardRef<

const inputRef = useRef<HTMLInputElement>(null);
const {
combobox: {
isOpen,
getMenuProps,
getInputProps,
highlightedIndex,
getItemProps,
inputValue,
setInputValue,
selectItem,
openMenu,
},
getMenuProps,
getInputProps,
getItemProps,
getSubmitButtonProps,
clear,
addingRecipe,
clearAddingRecipe,
onInputPaste,
} = useAddBarCombobox({
setSuggestionPrompt,
allSuggestions,
onAdd: (items) => {
onAdd: (items, focusInput) => {
onAdd(items);
inputRef.current?.blur();
if (focusInput) {
inputRef.current?.focus();
} else {
inputRef.current?.blur();
}
},
onOpenChange,
open,
});

const mergedRef = useMergedRef(ref, innerRef);
Expand All @@ -86,9 +85,16 @@ const AddPaneImpl = forwardRef<
visualViewport?.addEventListener('scroll', preventDefault, true);
const original = document.body.style.overflow;
document.body.style.overflow = 'hidden';
const pageContent = document.getElementById('page-content');
if (pageContent) {
pageContent.style.overflow = 'hidden';
}
return () => {
visualViewport?.removeEventListener('scroll', preventDefault);
document.body.style.overflow = original;
if (pageContent) {
pageContent.style.overflow = '';
}
};
}
}, [open]);
Expand All @@ -108,22 +114,20 @@ const AddPaneImpl = forwardRef<
>
<AddInput
inputProps={getInputProps({
onPaste: onInputPaste,
onPointerDown: openMenu,
placeholder,
ref: inputRef,
})}
isOpen={isOpen}
selectItem={selectItem}
clear={() => setInputValue('')}
isOpen={!!open}
clear={() => clear()}
ref={mergedRef}
disableInteraction={disabled}
getSubmitButtonProps={getSubmitButtonProps}
{...rest}
/>
<ScrollArea
{...menuProps}
className={classNames(
'flex flex-col max-h-[calc(var(--viewport-height,40dvh)-80px)] lg:max-h-50dvh w-full max-w-none gap-4',
'flex flex-col max-h-[calc(var(--viewport-height,40dvh)-40px)] lg:max-h-50dvh w-full max-w-none gap-4',
)}
onScroll={stopPropagation}
background="white"
Expand All @@ -133,7 +137,6 @@ const AddPaneImpl = forwardRef<
<SuggestionGroup
title="Suggested"
suggestions={mainSuggestions}
highlightedIndex={highlightedIndex}
getItemProps={getItemProps}
/>
)}
Expand All @@ -142,14 +145,12 @@ const AddPaneImpl = forwardRef<
title="Expiring Soon"
suggestions={expiresSoonSuggestions}
getItemProps={getItemProps}
highlightedIndex={highlightedIndex}
/>
)}
{!noSuggestions && (
<SuggestionGroup
title={inputValue ? 'Matches' : 'Favorites'}
title={suggestionPrompt ? 'Matches' : 'Favorites'}
suggestions={matchSuggestions}
highlightedIndex={highlightedIndex}
getItemProps={getItemProps}
/>
)}
Expand All @@ -169,7 +170,11 @@ const AddPaneImpl = forwardRef<

export const AddPane = forwardRef<
HTMLDivElement,
AddBarProps & { disabled?: boolean }
AddBarProps & {
disabled?: boolean;
open: boolean;
onOpenChange: (open: boolean) => void;
}
>(function AddBar(props, ref) {
return (
<Suspense>
Expand Down
Loading

0 comments on commit 132b936

Please sign in to comment.