Skip to content

Commit

Permalink
Add arrow navigation to remaining popover menus (#1001)
Browse files Browse the repository at this point in the history
This enables arrow navigation for the series block sorting menus and the
menus for adding block content.
Waiting for opencast/appkit#3, which is needed
in order for this to work.

Together they will close #753.
  • Loading branch information
LukasKalbertodt authored Jan 15, 2024
2 parents 4b7bf80 + ad1f0ed commit 2d2bc93
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 162 deletions.
8 changes: 4 additions & 4 deletions frontend/package-lock.json

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

2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"@babel/preset-typescript": "^7.22.15",
"@emotion/cache": "^11.11.0",
"@emotion/react": "^11.11.1",
"@opencast/appkit": "^0.1.4",
"@opencast/appkit": "^0.2.4",
"@svgr/webpack": "^8.1.0",
"babel-loader": "^9.1.3",
"babel-plugin-relay": "^15.0.0",
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ type Props = {
export const App: React.FC<Props> = ({ initialRoute }) => (
<StrictMode>
<SilenceEmotionWarnings>
<ColorSchemeProvider>
<ColorSchemeProvider allowedSchemes={["light", "dark"]}>
<AppkitConfigProvider config={{
...DEFAULT_CONFIG,
colors: {
Expand Down
195 changes: 100 additions & 95 deletions frontend/src/routes/manage/Realm/Content/AddButtons.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import React, { useRef } from "react";
import React, { RefObject, useId, useRef } from "react";
import { useTranslation } from "react-i18next";
import { graphql, useFragment, commitLocalUpdate, useRelayEnvironment } from "react-relay";
import type { RecordProxy, RecordSourceProxy } from "relay-runtime";
import { LuPlus, LuHash, LuType, LuGrid, LuFilm } from "react-icons/lu";
import { LuPlus, LuHash, LuType, LuFilm, LuLayoutGrid } from "react-icons/lu";
import {
ProtoButton, bug, useColorScheme,
Floating, FloatingContainer, FloatingHandle, FloatingTrigger, WithTooltip,
ProtoButton, bug, useColorScheme, Floating, FloatingContainer,
FloatingHandle, FloatingTrigger, WithTooltip, useFloatingItemProps,
} from "@opencast/appkit";

import { AddButtonsRealmData$key } from "./__generated__/AddButtonsRealmData.graphql";
Expand All @@ -21,41 +21,8 @@ type Props = {

export const AddButtons: React.FC<Props> = ({ index, realm }) => {
const { t } = useTranslation();
const isDark = useColorScheme().scheme === "dark";

const floatingRef = useRef<FloatingHandle>(null);

const { id: realmId } = useFragment(graphql`
fragment AddButtonsRealmData on Realm {
id
}
`, realm);

const env = useRelayEnvironment();

const addBlock = (
type: string,
prepareBlock?: (store: RecordSourceProxy, block: RecordProxy) => void,
) => {
commitLocalUpdate(env, store => {
const realm = store.get(realmId) ?? bug("could not find realm");

const blocks = [
...realm.getLinkedRecords("blocks") ?? bug("realm does not have blocks"),
];

const id = "clNEWBLOCK";
const block = store.create(id, `${type}Block`);
prepareBlock?.(store, block);
block.setValue(true, "editMode");
block.setValue(id, "id");

blocks.splice(index, 0, block);

realm.setLinkedRecords(blocks, "blocks");
});
};

const BUTTON_SIZE = 36;

return (
Expand Down Expand Up @@ -99,81 +66,119 @@ export const AddButtons: React.FC<Props> = ({ index, realm }) => {
</WithTooltip>
</div>
</FloatingTrigger>

<Floating
padding={0}
borderWidth={isDark ? 1 : 0}
backgroundColor={isDark ? COLORS.neutral15 : COLORS.neutral05}
shadowBlur={12}
shadowColor="rgba(0, 0, 0, 30%)"
css={{ width: 200 }}
>
<div css={{
fontSize: 14,
color: COLORS.neutral60,
padding: "8px 16px",
cursor: "default",
}}>{t("manage.realm.content.add-popup-title")}</div>
<ul css={{
listStyle: "none",
margin: 0,
padding: 0,
"& > li:not(:last-child)": {
borderBottom: `1px solid ${isDark ? COLORS.neutral25 : COLORS.neutral15}`,
},
}}>
<AddItem
close={() => floatingRef.current?.close()}
Icon={LuHash}
label={t("manage.realm.content.add-title")}
onClick={() => addBlock("Title")}
/>
<AddItem
close={() => floatingRef.current?.close()}
Icon={LuType}
label={t("manage.realm.content.add-text")}
onClick={() => addBlock("Text")}
/>
<AddItem
close={() => floatingRef.current?.close()}
Icon={LuGrid}
label={t("manage.realm.content.add-series")}
onClick={() => addBlock("Series", (_store, block) => {
block.setValue("NEW_TO_OLD", "order");
block.setValue(true, "showTitle");
block.setValue(false, "showMetadata");
})}
/>
<AddItem
close={() => floatingRef.current?.close()}
Icon={LuFilm}
label={t("manage.realm.content.add-video")}
onClick={() => addBlock("Video", (_store, block) => {
block.setValue(true, "showTitle");
block.setValue(true, "showLink");
})}
/>
</ul>
</Floating>
<AddButtonsMenu {...{ index, realm, floatingRef }} />
</FloatingContainer>
);
};

const AddButtonsMenu: React.FC<Props & {floatingRef: RefObject<FloatingHandle>}> = ({
index, realm, floatingRef,
}) => {
const isDark = useColorScheme().scheme === "dark";
const { t } = useTranslation();
const itemProps = useFloatingItemProps();
const menuId = useId();

const { id: realmId } = useFragment(graphql`
fragment AddButtonsRealmData on Realm {
id
}
`, realm);

const env = useRelayEnvironment();

const addBlock = (
type: string,
prepareBlock?: (store: RecordSourceProxy, block: RecordProxy) => void,
) => {
commitLocalUpdate(env, store => {
const realm = store.get(realmId) ?? bug("could not find realm");

const blocks = [
...realm.getLinkedRecords("blocks") ?? bug("realm does not have blocks"),
];

const id = "clNEWBLOCK";
const block = store.create(id, `${type}Block`);
prepareBlock?.(store, block);
block.setValue(true, "editMode");
block.setValue(id, "id");

blocks.splice(index, 0, block);

realm.setLinkedRecords(blocks, "blocks");
});
};

type Block = "title" | "text" | "series" | "video";
const buttonProps: [IconType, Block, () => void][] = [
[LuHash, "title", () => addBlock("Title")],
[LuType, "text", () => addBlock("Text")],
[LuLayoutGrid, "series", () => addBlock("Series", (_store, block) => {
block.setValue("NEW_TO_OLD", "order");
block.setValue(true, "showTitle");
block.setValue(false, "showMetadata");
})],
[LuFilm, "video", () => addBlock("Video", (_store, block) => {
block.setValue(true, "showTitle");
block.setValue(true, "showLink");
})],
];

return (
<Floating
padding={0}
borderWidth={isDark ? 1 : 0}
backgroundColor={isDark ? COLORS.neutral15 : COLORS.neutral05}
shadowBlur={12}
shadowColor="rgba(0, 0, 0, 30%)"
css={{ width: 200 }}
>
<div css={{
fontSize: 14,
color: COLORS.neutral60,
padding: "8px 16px",
cursor: "default",
}}>{t("manage.realm.content.add-popup-title")}</div>
<ul css={{
listStyle: "none",
margin: 0,
padding: 0,
"& > li:not(:last-child)": {
borderBottom: `1px solid ${isDark ? COLORS.neutral25 : COLORS.neutral15}`,
},
}}>
{buttonProps.map(([icon, type, onClick], index) => <AddItem
key={`${menuId}-${type}`}
close={() => floatingRef.current?.close()}
Icon={icon}
label={t(`manage.realm.content.add-${type}`)}
{...itemProps(index)}
{...{ onClick }}
/>)}
</ul>
</Floating>
);
};

type AddItemProps = {
label: string;
Icon: IconType;
onClick: () => void;
close: () => void;
};

const AddItem: React.FC<AddItemProps> = ({ label, Icon, onClick, close }) => (
const AddItem = React.forwardRef<HTMLButtonElement, AddItemProps>(({
label, Icon, onClick, close,
}, ref) => (
<li role="menuitem" css={{
"&:last-child > button": {
borderBottomLeftRadius: 8,
borderBottomRightRadius: 8,
},
}}>
<ProtoButton
{...{ ref }}
onClick={() => {
onClick();
close();
Expand All @@ -196,4 +201,4 @@ const AddItem: React.FC<AddItemProps> = ({ label, Icon, onClick, close }) => (
{label}
</ProtoButton>
</li>
);
));
Loading

0 comments on commit 2d2bc93

Please sign in to comment.