Skip to content

Commit

Permalink
Merge pull request #832 from trey-wallis/dev
Browse files Browse the repository at this point in the history
8.10.0

Former-commit-id: 4c1dd37
  • Loading branch information
decaf-dev authored Nov 1, 2023
2 parents bfc5084 + 54a6023 commit 5e4ad12
Show file tree
Hide file tree
Showing 12 changed files with 274 additions and 90 deletions.
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@
"fundingUrl": {
"Buymeacoffee": "https://www.buymeacoffee.com/treywallis"
},
"version": "8.9.3"
"version": "8.10.0"
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "obsidian-dataloom",
"version": "8.9.3",
"version": "8.10.0",
"description": "Weave together data from diverse sources into a cohesive table view. Inspired by Excel Spreadsheets and Notion.so.",
"main": "main.js",
"scripts": {
Expand Down
67 changes: 28 additions & 39 deletions src/react/loom-app/app/hooks/use-menu-events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,35 @@ import { useMenuOperations } from "src/react/shared/menu-provider/hooks";
export const useMenuEvents = () => {
useCloseOnOutsideClick();
useCloseOnObsidianModalOpen();
useCloseOnTableScroll();
useCloseOnMarkdownViewScroll();
useStopTableScroll();
};

const useStopTableScroll = () => {
const { reactAppId } = useAppMount();
const { topMenu } = useMenuOperations();

React.useEffect(() => {
const appEl = document.getElementById(reactAppId);
if (!appEl) return;

const tableContainerEl = appEl.querySelector(
'[data-virtuoso-scroller="true"]'
) as HTMLElement | null;
if (!tableContainerEl) return;

const { parentComponentId } = topMenu ?? {};
//Only stop table scroll when the menu is opened from a cell
if (!parentComponentId?.includes("cell")) return;

if (topMenu) {
tableContainerEl.style.overflowX = "hidden";
tableContainerEl.style.overflowY = "hidden";
} else {
tableContainerEl.style.overflowX = "auto";
tableContainerEl.style.overflowY = "auto";
}
}, [topMenu]);
};

/**
Expand Down Expand Up @@ -57,44 +84,6 @@ const useCloseOnMarkdownViewScroll = () => {
}, [onCloseAll, isMarkdownView, reactAppId]);
};

const useCloseOnTableScroll = () => {
const { reactAppId } = useAppMount();
const { onCloseAll } = useMenuOperations();

React.useEffect(() => {
const THROTTLE_TIME_MILLIS = 100;
const throttleHandleScroll = _.throttle(
handleScroll,
THROTTLE_TIME_MILLIS
);

function handleScroll() {
//Find any open menus
const openMenus = document.querySelectorAll(".dataloom-menu");
if (openMenus.length === 0) return;

//Since it takes a noticable amount of time for React to update the DOM, we set
//the display to none and then wait for React to clean up the DOM
for (const menu of openMenus) {
(menu as HTMLElement).style.display = "none";
}
onCloseAll();
}

const appEl = document.getElementById(reactAppId);
if (!appEl) return;

const tableContainer = appEl.querySelector(
'[data-virtuoso-scroller="true"]'
) as HTMLElement | null;
if (!tableContainer) return;

tableContainer.addEventListener("scroll", throttleHandleScroll);
return () =>
tableContainer?.removeEventListener("scroll", throttleHandleScroll);
}, [onCloseAll, reactAppId]);
};

/**
* If the user clicks outside the app, close all menus
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.dataloom-number-format-submenu {
width: 100%;
height: 240px;
overflow-y: scroll;
overflow-x: auto;
overflow-y: scroll;
}
26 changes: 15 additions & 11 deletions src/react/loom-app/option-bar/filter-menu/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ import {
createTextFilter,
} from "src/shared/loom-state/loom-state-factory";
import DateFilterSelect from "./date-filter-select";
import Tag from "src/react/shared/tag";

interface Props {
id: string;
Expand Down Expand Up @@ -497,20 +498,23 @@ export default function FilterMenu({
const { tagIds } = filter as MultiTagFilter;
inputNode = (
<MultiSelect
value={tagIds}
id={id}
title="Tags"
options={tags.map((tag) => ({
id: tag.id,
name: tag.content,
component: (
<Tag
content={tag.content}
color={tag.color}
/>
),
}))}
selectedOptionIds={tagIds}
onChange={(value) =>
onTagsChange(id, value)
}
>
{tags.map((tag) => (
<option
key={tag.id}
value={tag.id}
>
{tag.content}
</option>
))}
</MultiSelect>
/>
);
conditionOptions = [
TextFilterCondition.CONTAINS,
Expand Down
19 changes: 15 additions & 4 deletions src/react/shared/menu-provider/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,15 @@ export default function MenuProvider({ children }: Props) {

const { name, shouldRequestOnClose } = options ?? {};

if (!triggerRef.current) return;
if (!canOpen(level)) return;
if (!triggerRef.current) {
logger("No trigger ref. Cannot open menu");
return;
}
if (!canOpen(level)) {
logger("Level is too low. Cannot open menu");
return;
}
logger("MenuProvider opening menu", { level });

const position = getPositionFromEl(triggerRef.current);
const menu = createMenu(parentComponentId, level, position, {
Expand All @@ -95,7 +102,10 @@ export default function MenuProvider({ children }: Props) {

const focusMenuTrigger = React.useCallback(
(parentComponentId: string, name?: string) => {
logger("MenuProvider focusMenuTrigger");
logger("MenuProvider focusMenuTrigger", {
parentComponentId,
name,
});
setFocusedMenuTrigger({
parentComponentId,
name,
Expand Down Expand Up @@ -194,6 +204,7 @@ export default function MenuProvider({ children }: Props) {
}

const handleCloseAll = React.useCallback(() => {
logger("MenuProvider onCloseAll");
setOpenMenus((prevState) =>
prevState.filter((menu) => menu.shouldRequestOnClose)
);
Expand All @@ -202,7 +213,7 @@ export default function MenuProvider({ children }: Props) {
createCloseRequest(menu.id, "save-and-close")
);
});
}, [openMenus, setOpenMenus]);
}, [openMenus, setOpenMenus, logger]);

const getTopMenu = React.useCallback(() => {
return openMenus[openMenus.length - 1] ?? null;
Expand Down
82 changes: 53 additions & 29 deletions src/react/shared/multi-select/index.tsx
Original file line number Diff line number Diff line change
@@ -1,43 +1,67 @@
import Stack from "../stack";
import Text from "../text";
import Icon from "../icon";

import MultiSelectMenu from "./multi-select-menu";
import { useMenu } from "../menu-provider/hooks";
import { LoomMenuLevel } from "../menu-provider/types";
import MenuTrigger from "../menu-trigger";
import { MultiSelectOptionType } from "./types";

import "./styles.css";

interface Props {
className?: string;
value: string[];
onKeyDown?: (e: React.KeyboardEvent<HTMLSelectElement>) => void;
onChange: (value: string[]) => void;
children: React.ReactNode;
id: string;
title: string;
selectedOptionIds: string[];
options: MultiSelectOptionType[];
onChange: (keys: string[]) => void;
}

export default function MultiSelect({
className: customClassName,
value,
id,
title,
selectedOptionIds,
options,
onChange,
onKeyDown,
children,
}: Props) {
function handleChange(e: React.ChangeEvent<HTMLSelectElement>) {
const selectedValues = Array.from(
e.target.selectedOptions,
(option) => option.value
);
onChange(selectedValues);
}
const COMPONENT_ID = `multi-select-${id}`;
const menu = useMenu(COMPONENT_ID);

let className = "dataloom-multi-select dataloom-focusable";
if (customClassName) {
className += " " + customClassName;
function openMenu() {
menu.onOpen(LoomMenuLevel.TWO);
}

return (
<select
tabIndex={0}
multiple={true}
className={className}
value={value}
onChange={handleChange}
onKeyDown={onKeyDown}
>
{children}
</select>
<>
<MenuTrigger
ref={menu.triggerRef}
variant="cell"
menuId={menu.id}
level={LoomMenuLevel.TWO}
isFocused={menu.isTriggerFocused}
onOpen={() => openMenu()}
>
<div className="dataloom-multi-select">
<Stack
isHorizontal
justify="space-between"
align="center"
height="100%"
>
<Text value={`${selectedOptionIds.length} ${title}`} />
<Icon lucideId="chevron-down" />
</Stack>
</div>
</MenuTrigger>
<MultiSelectMenu
id={menu.id}
isOpen={menu.isOpen}
position={menu.position}
options={options}
selectedOptionIds={selectedOptionIds}
onChange={onChange}
/>
</>
);
}
86 changes: 86 additions & 0 deletions src/react/shared/multi-select/multi-select-menu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import Stack from "../stack";
import Text from "../text";
import Menu from "../menu";
import { LoomMenuPosition } from "../menu/types";
import MultiSelectOption from "./multi-select-option";
import { MultiSelectOptionType } from "./types";
import Input from "../input";
import React from "react";
import Padding from "../padding";

interface Props {
id: string;
isOpen: boolean;
position: LoomMenuPosition;
options: MultiSelectOptionType[];
selectedOptionIds: string[];
onChange: (keys: string[]) => void;
}

export default function MultiSelectMenu({
id,
isOpen,
position,
options,
selectedOptionIds,
onChange,
}: Props) {
const [inputValue, setInputValue] = React.useState("");

React.useEffect(() => {
if (!isOpen) {
setInputValue("");
}
}, [isOpen]);

function handleOptionClick(id: string) {
const isSelected = selectedOptionIds.includes(id);
if (isSelected) {
const filteredOptionIds = selectedOptionIds.filter(
(selected) => selected !== id
);
onChange(filteredOptionIds);
} else {
onChange([...selectedOptionIds, id]);
}
}

const filteredOptions = options.filter((option) =>
option.name.toLowerCase().includes(inputValue.toLocaleLowerCase())
);
return (
<Menu id={id} isOpen={isOpen} position={position} topOffset={35}>
<div className="dataloom-multi-select-menu">
<Stack spacing="md">
<Padding px="md" pt="sm">
<Input
value={inputValue}
placeholder="Type to filter..."
onChange={setInputValue}
/>
</Padding>
<div className="dataloom-multi-select__options">
{filteredOptions.map((option) => {
const { id, component } = option;
const isChecked = selectedOptionIds.includes(id);
return (
<MultiSelectOption
key={id}
id={id}
isChecked={isChecked}
component={component}
handleOptionClick={handleOptionClick}
/>
);
})}
{options.length === 0 && (
<Padding px="md" pb="sm">
<Text value="No options to select" />
</Padding>
)}
</div>
</Stack>
</div>
</Menu>
);
}
Loading

0 comments on commit 5e4ad12

Please sign in to comment.