Skip to content

Commit

Permalink
#25 Add select, number and checkbox columns
Browse files Browse the repository at this point in the history
  • Loading branch information
Polleps committed Mar 3, 2023
1 parent f8b09b1 commit 42886ff
Show file tree
Hide file tree
Showing 41 changed files with 1,355 additions and 180 deletions.
2 changes: 2 additions & 0 deletions data-browser/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@
"@dnd-kit/core": "^4.0.3",
"@dnd-kit/sortable": "^5.1.0",
"@dnd-kit/utilities": "^3.0.2",
"@emoji-mart/react": "^1.1.1",
"@radix-ui/react-popover": "^1.0.2",
"@radix-ui/react-scroll-area": "^1.0.1",
"@tomic/react": "workspace:*",
"emoji-mart": "^5.5.2",
"polished": "^4.1.0",
"query-string": "^7.0.0",
"quick-score": "^0.0.10",
Expand Down
9 changes: 6 additions & 3 deletions data-browser/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { ErrorBoundary } from './views/ErrorPage';
import { NetworkIndicator } from './components/NetworkIndicator';
import { getAgentFromLocalStorage } from './helpers/agentStorage';
import { DropdownContainer } from './components/Dropdown/DropdownContainer';
import { PopoverContainer } from './components/Popover';

function fixDevUrl(url: string) {
if (isDev()) {
Expand Down Expand Up @@ -82,9 +83,11 @@ function App(): JSX.Element {
<MetaSetter />
<DropdownContainer>
<DialogContainer>
<NavWrapper>
<AppRoutes />
</NavWrapper>
<PopoverContainer>
<NavWrapper>
<AppRoutes />
</NavWrapper>
</PopoverContainer>
<NetworkIndicator />
</DialogContainer>
</DropdownContainer>
Expand Down
99 changes: 99 additions & 0 deletions data-browser/src/chunks/EmojiInput/EmojiInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import React, { useCallback, useState } from 'react';
import Picker from '@emoji-mart/react';
import styled from 'styled-components';
import * as RadixPopover from '@radix-ui/react-popover';
import { transition } from '../../helpers/transition';
import { Popover } from '../../components/Popover';

interface EmojiInputProps {
initialValue?: string;
onChange: (value: string | undefined) => void;
}

const EMOJI_DATA_URL = 'https://cdn.jsdelivr.net/npm/@emoji-mart/data';

let data: Promise<unknown>;

const fetchAndCacheData = async () => {
if (data) {
return data;
}

const response = await fetch(EMOJI_DATA_URL);
data = response.json();

return data;
};

export default function EmojiInput({
initialValue,
onChange,
}: EmojiInputProps): JSX.Element {
const [showPicker, setShowPicker] = useState(false);
const [emoji, setEmoji] = useState<string | undefined>(initialValue);

const handleEmojiSelect = useCallback((e: any) => {
setEmoji(e.native);
setShowPicker(false);
onChange(e.native);
}, []);

return (
<PickerPopover
noArrow
open={showPicker}
onOpenChange={setShowPicker}
Trigger={
<PickerButton onClick={() => setShowPicker(true)}>
{emoji ? <Preview>{emoji}</Preview> : <Placeholder>😎</Placeholder>}
</PickerButton>
}
>
<PickerWrapper>
<Picker
autoFocus
data={fetchAndCacheData}
onEmojiSelect={handleEmojiSelect}
maxFrequentRows={2}
dynamicWidth={true}
/>
</PickerWrapper>
</PickerPopover>
);
}

const Preview = styled.span`
transition: ${transition('font-size')};
`;

const Placeholder = styled(Preview)`
opacity: 0.5;
`;

const PickerButton = styled(RadixPopover.Trigger)`
border: none;
border-radius: ${({ theme }) => theme.radius};
width: 2rem;
background: transparent;
padding: 0;
cursor: pointer;
user-select: none;
&:hover > ${Preview} {
font-size: 1.3rem;
}
`;

const PickerPopover = styled(Popover)`
top: 200px;
`;

const PickerWrapper = styled.div`
display: contents;
& em-emoji-picker {
height: 400px;
width: min(90vw, 20rem);
}
`;
7 changes: 0 additions & 7 deletions data-browser/src/components/Dialog/dialogContext.ts

This file was deleted.

42 changes: 42 additions & 0 deletions data-browser/src/components/Dialog/dialogContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React, { createContext, useMemo, useState } from 'react';

export const DialogPortalContext = createContext<
React.RefObject<HTMLDivElement>
>(null!);

interface DialogTreeContext {
inDialog: boolean;
hasOpenInnerPopup: boolean;
setHasOpenInnerPopup: React.Dispatch<React.SetStateAction<boolean>>;
}

export const DialogTreeContext = createContext<DialogTreeContext>({
inDialog: false,
hasOpenInnerPopup: false,
setHasOpenInnerPopup: () => undefined,
});

export const DialogTreeContextProvider: React.FC<React.PropsWithChildren> = ({
children,
}) => {
const [hasOpenInnerPopup, setHasOpenInnerPopup] = useState<boolean>(false);

const context = useMemo(
() => ({
inDialog: true,
hasOpenInnerPopup,
setHasOpenInnerPopup,
}),
[hasOpenInnerPopup],
);

return (
<DialogTreeContext.Provider value={context}>
{children}
</DialogTreeContext.Provider>
);
};

export function useDialogTreeContext() {
return React.useContext(DialogTreeContext);
}
27 changes: 20 additions & 7 deletions data-browser/src/components/Dialog/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,13 @@ import { FaTimes } from 'react-icons/fa';
import styled, { keyframes } from 'styled-components';
import { effectTimeout } from '../../helpers/effectTimeout';
import { Button } from '../Button';
import { PopoverContainer } from '../Popover';
import { Slot } from '../Slot';
import { DialogPortalContext, DialogTreeContext } from './dialogContext';
import {
DialogPortalContext,
DialogTreeContextProvider,
useDialogTreeContext,
} from './dialogContext';
import { useDialog } from './useDialog';

export interface InternalDialogProps {
Expand Down Expand Up @@ -55,7 +60,14 @@ type DialogSlotComponent = React.FC<React.PropsWithChildren<unknown>>;
* );
* ```
*/
export const Dialog: React.FC<React.PropsWithChildren<InternalDialogProps>> = ({
export const Dialog: React.FC<React.PropsWithChildren<InternalDialogProps>> =
props => (
<DialogTreeContextProvider>
<InnerDialog {...props} />
</DialogTreeContextProvider>
);

const InnerDialog: React.FC<React.PropsWithChildren<InternalDialogProps>> = ({
children,
show,
onClose,
Expand All @@ -64,6 +76,7 @@ export const Dialog: React.FC<React.PropsWithChildren<InternalDialogProps>> = ({
const dialogRef = useRef<HTMLDialogElement>(null);
const innerDialogRef = useRef<HTMLDivElement>(null);
const portalRef = useContext(DialogPortalContext);
const { hasOpenInnerPopup } = useDialogTreeContext();

const cancelDialog = useCallback(() => {
onClose(false);
Expand All @@ -89,7 +102,7 @@ export const Dialog: React.FC<React.PropsWithChildren<InternalDialogProps>> = ({
() => {
cancelDialog();
},
{ enabled: show },
{ enabled: show && !hasOpenInnerPopup },
);

// When closing the `data-closing` attribute must be set before rendering so the animation has started when the regular useEffect is called.
Expand Down Expand Up @@ -127,16 +140,16 @@ export const Dialog: React.FC<React.PropsWithChildren<InternalDialogProps>> = ({

return createPortal(
<StyledDialog ref={dialogRef} onMouseDown={handleOutSideClick}>
<DialogTreeContext.Provider value={true}>
<StyledInnerDialog ref={innerDialogRef}>
<StyledInnerDialog ref={innerDialogRef}>
<PopoverContainer>
<CloseButtonSlot slot='close'>
<Button icon onClick={cancelDialog} aria-label='close'>
<FaTimes />
</Button>
</CloseButtonSlot>
{children}
</StyledInnerDialog>
</DialogTreeContext.Provider>
</PopoverContainer>
</StyledInnerDialog>
</StyledDialog>,
portalRef.current,
);
Expand Down
6 changes: 1 addition & 5 deletions data-browser/src/components/Dropdown/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { shortcuts } from '../HotKeyWrapper';
import { Shortcut } from '../Shortcut';
import { createPortal } from 'react-dom';
import { DropdownPortalContext } from './dropdownContext';
import { loopingIndex } from '../../helpers/loopingIndex';

export const DIVIDER = 'divider' as const;

Expand All @@ -39,11 +40,6 @@ interface DropdownMenuProps {
isMainMenu?: boolean;
}

/** Gets the index of an array and loops around when at the beginning or end */
const loopingIndex = (index: number, length: number) => {
return ((index % length) + length) % length;
};

export const isItem = (
item: MenuItemMinimial | string | undefined,
): item is MenuItemMinimial =>
Expand Down
9 changes: 5 additions & 4 deletions data-browser/src/components/IconButton/IconButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ export enum IconButtonVariant {

type ColorProp = keyof DefaultTheme['colors'] | 'inherit';

interface BaseProps {
type BaseProps = {
className?: string;
variant?: IconButtonVariant;
color?: ColorProp;
size?: string;
title: string;
}
as?: string | React.ComponentType<any>;
};

export type IconButtonProps = BaseProps &
React.ButtonHTMLAttributes<HTMLButtonElement>;
Expand Down Expand Up @@ -66,11 +67,11 @@ IconButtonLink.displayName = 'IconButtonLink';

IconButtonLink.defaultProps = defaultProps as IconButtonLinkProps;

interface BaseProps {
interface ButtonBaseProps {
size?: string;
}

const IconButtonBase = styled.button<BaseProps>`
const IconButtonBase = styled.button<ButtonBaseProps>`
--button-padding: 0.4em;
cursor: pointer;
display: inline-grid;
Expand Down
49 changes: 49 additions & 0 deletions data-browser/src/components/PalettePicker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React from 'react';
import styled from 'styled-components';
import { transition } from '../helpers/transition';
import { Row } from './Row';

interface PalettePickerProps {
palette: string[];
onChange: (color: string) => void;
}

export function PalettePicker({
palette,
onChange,
}: PalettePickerProps): JSX.Element {
const createHandleClick = (color: string) => () => onChange(color);

return (
<Row wrapItems>
{palette.map(color => (
<PaletteButton
key={color}
color={color}
onClick={createHandleClick(color)}
></PaletteButton>
))}
</Row>
);
}

interface PaletteButtonProps {
color: string;
}

const PaletteButton = styled.button<PaletteButtonProps>`
background-color: ${({ color }) => color};
border: none;
height: 1.5rem;
aspect-ratio: 1/1;
border-radius: 50%;
cursor: pointer;
transform-origin: center;
transition: ${transition('transform')};
&:hover,
&:focus {
outline: none;
transform: scale(1.3);
}
`;
Loading

0 comments on commit 42886ff

Please sign in to comment.