Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions tavern/internal/www/build/asset-manifest.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion tavern/internal/www/build/index.html

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions tavern/internal/www/build/static/css/main.739fc8dd.css.map

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion tavern/internal/www/build/static/css/main.dfbaa24a.css.map

This file was deleted.

3 changes: 0 additions & 3 deletions tavern/internal/www/build/static/js/main.06eb70b1.js

This file was deleted.

3 changes: 3 additions & 0 deletions tavern/internal/www/build/static/js/main.0d049a8c.js

Large diffs are not rendered by default.

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion tavern/internal/www/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { FilterProvider } from "./context/FilterContext";
import { Tomes } from "./pages/tomes/Tomes";
import { AdminPortal } from "./pages/admin/AdminPortal";
import { CreateQuest } from "./pages/create-quest/CreateQuest";
import { SortsProvider } from "./context/SortContext";


const router = createBrowserRouter([
Expand Down Expand Up @@ -77,7 +78,9 @@ export const App = () => {
<TagContextProvider>
<UserPreferencesContextProvider>
<FilterProvider>
<RouterProvider router={router} />
<SortsProvider>
<RouterProvider router={router} />
</SortsProvider>
</FilterProvider>
</UserPreferencesContextProvider>
</TagContextProvider>
Expand Down
84 changes: 84 additions & 0 deletions tavern/internal/www/src/components/ButtonDialogPopover.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import React, { ReactElement, useEffect, useRef, useState } from 'react';
import { Dialog } from '@headlessui/react';
import Button from './tavern-base-ui/button/Button';

type Position = {
top: number
left: number
}

export const ButtonDialogPopover = ({ children, label, leftIcon }: {
children: ReactElement,
label: string,
leftIcon: ReactElement
}) => {
const [isOpen, setIsOpen] = useState(false)
const [position, setPosition] = useState<Position>({ top: 0, left: 0 })

const buttonRef = useRef<HTMLButtonElement | null>(null);
const panelRef = useRef<HTMLDivElement | null>(null);

const openDialog = (): void => {
const dialogWidth = 384;
const spacing = 8;

const button = buttonRef.current
if (button) {
const rect = button.getBoundingClientRect()
const viewportWidth = window.innerWidth

const calculatedLeft = Math.min(
rect.left + window.scrollX,
viewportWidth - dialogWidth - 16 // 16px right margin
)

setPosition({
top: rect.bottom + window.scrollY + spacing,
left: calculatedLeft,
})
}
setIsOpen(true)
}

const closeDialog = (): void => setIsOpen(false)

useEffect(() => {
if (!isOpen) return

const handleClickOutside = (event: MouseEvent): void => {
const target = event.target as Node
if (
panelRef.current &&
!panelRef.current.contains(target) &&
buttonRef.current &&
!buttonRef.current.contains(target)
) {
closeDialog()
}
}

document.addEventListener('mousedown', handleClickOutside)
return () => document.removeEventListener('mousedown', handleClickOutside)
}, [isOpen])

return (
<div className='flex justify-end'>
<Button ref={buttonRef} leftIcon={leftIcon} buttonVariant='ghost' buttonStyle={{ color: "gray", size: "md" }} onClick={openDialog}>{label}</Button>
<Dialog open={isOpen} onClose={closeDialog}>
<div>
<div className="fixed inset-0 bg-black/30 z-40" aria-hidden="true" />
<div
ref={panelRef}
className="absolute z-50 bg-white border rounded-lg shadow-lg w-96 p-4 flex flex-col gap-4"
style={{
top: position.top,
left: position.left,
}}
>
{children}
</div>
</div>
</Dialog>
</div>
);
}
99 changes: 99 additions & 0 deletions tavern/internal/www/src/components/SortingControls.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { BarsArrowDownIcon, BarsArrowUpIcon } from "@heroicons/react/24/outline";
import { useSorts } from "../context/SortContext";
import { HostOrderField, OrderDirection, PageNavItem, QuestOrderField, TaskOrderField } from "../utils/enums";
import { ButtonDialogPopover } from "./ButtonDialogPopover";
import SingleDropdownSelector, { Option } from "./tavern-base-ui/SingleDropdownSelector";
import { ReactElement } from "react";

const orderFieldOptionsMap = {
[PageNavItem.hosts]: createOrderFieldOptions(HostOrderField),
[PageNavItem.quests]: createOrderFieldOptions(QuestOrderField),
[PageNavItem.tasks]: createOrderFieldOptions(TaskOrderField),
};

const directionOptions = [
{
label: "Ascending",
value: OrderDirection.Asc
},
{
label: "Descending",
value: OrderDirection.Desc
}
];


function formatEnumLabel(enumValue: string): string {
return enumValue
.split('_')
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
.join(' ');
}

function getDirectionField(direction: OrderDirection): Option {
if (direction === OrderDirection.Asc) {
return directionOptions[0];
};
return directionOptions[1];
}

function getDirectionIcon(direction: OrderDirection): ReactElement {
if (direction === OrderDirection.Asc) {
return <BarsArrowUpIcon className="w-4" />

};
return <BarsArrowDownIcon className="w-4" />
}

function createOrderFieldOptions<T extends QuestOrderField | TaskOrderField | HostOrderField>(
enumObj: Record<string, T>
): Array<Option> {
return Object.values(enumObj).map((field) => ({
label: formatEnumLabel(field),
value: field,
}));
}


export default function SortingControls({ type }: { type: PageNavItem.hosts | PageNavItem.quests | PageNavItem.tasks }) {
const { sorts, updateSorts } = useSorts();

const sortFieldsInUse = sorts[type];

const activeFieldSortOption = {
label: formatEnumLabel(sortFieldsInUse.field),
value: sortFieldsInUse.field,
};

const activeDirectionSortOption = getDirectionField(sortFieldsInUse.direction);

const orderFieldOptions = orderFieldOptionsMap[type];

const leftIcon = getDirectionIcon(sortFieldsInUse.direction)

return (
<ButtonDialogPopover label={`Sort (${activeFieldSortOption.label})`} leftIcon={leftIcon}>
<div className="flex flex-col gap-1">
<div className="flex flex-row justify-between pb-2 border-gray-100 border-b-2">
<h3 className="font-medium text-lg text-gray-700">Sorting</h3>
</div>
<div className="grid grid-cols-2 gap-2">
<SingleDropdownSelector
label="Field"
options={orderFieldOptions}
setSelectedOption={(option: Option) => updateSorts({ [type]: { field: option.value, direction: activeDirectionSortOption.value } })}
isSearchable={false}
defaultValue={activeFieldSortOption}
/>
<SingleDropdownSelector
label="Direction"
options={directionOptions}
setSelectedOption={(option: Option) => updateSorts({ [type]: { field: activeFieldSortOption.value, direction: option.value } })}
isSearchable={false}
defaultValue={activeDirectionSortOption}
/>
</div>
</div>
</ButtonDialogPopover>
)
};
Original file line number Diff line number Diff line change
Expand Up @@ -30,25 +30,26 @@ export const CreateQuestDropdown = ({
return (
<Menu as="div" >
<div>
<Menu.Button className="inline-flex w-full justify-center">
{showLabel ?
<Button
buttonStyle={{ color: 'purple', size: "md" }}
rightIcon={<ChevronDownIcon
className="h-5 w-5"
aria-hidden="true"
/>}
>
Re-run quest
</Button>
:
<Button
leftIcon={<EllipsisHorizontalIcon
className="h-5 w-5"
aria-hidden="true"
/>} />
}
</Menu.Button>
{showLabel ?
<Menu.Button
as={Button}
buttonStyle={{ color: 'purple', size: "md" }}
rightIcon={<ChevronDownIcon
className="h-5 w-5"
aria-hidden="true"
/>}
>
Re-run quest
</Menu.Button>
:
<Menu.Button
as={Button}
leftIcon={<EllipsisHorizontalIcon
className="h-5 w-5"
aria-hidden="true"
/>}
/>
}
</div>
<Transition
as={Fragment}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
import { FC } from "react";
import Select from "react-select"

type Option = {
export type Option = {
label: string;
value: string;
}
type SingleDropdownSelectorProps = {
label: string;
options: Array<Option>;
setSelectedOption: any;
isSearchable?: boolean;
defaultValue?: Option
}
const SingleDropdownSelector: FC<SingleDropdownSelectorProps> = ({
label,
options,
setSelectedOption
setSelectedOption,
isSearchable = true,
defaultValue
}) => {

const styles = {
Expand Down Expand Up @@ -63,8 +67,9 @@ const SingleDropdownSelector: FC<SingleDropdownSelectorProps> = ({
return (
<div className="min-w-[140px]">
<Select
isSearchable={isSearchable}
styles={styles}
defaultValue={options[0]}
defaultValue={defaultValue || options[0]}
onChange={setSelectedOption}
name={label}
options={options}
Expand Down
Loading
Loading