Skip to content

Commit

Permalink
feat: context menu for multiselect
Browse files Browse the repository at this point in the history
  • Loading branch information
liuycy committed Mar 7, 2024
1 parent 8ae68a2 commit ba8fa12
Show file tree
Hide file tree
Showing 8 changed files with 142 additions and 67 deletions.
4 changes: 3 additions & 1 deletion src/pages/home/folder/Grid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import { local, objStore } from "~/store"
import { useSelectWithMouse } from "./helper"

const GridLayout = () => {
const { isMouseSupported, registerSelectContainer } = useSelectWithMouse()
const { isMouseSupported, registerSelectContainer, captureContentMenu } =
useSelectWithMouse()
registerSelectContainer()
return (
<Grid
oncapture:contextmenu={captureContentMenu}
classList={{ "viselect-container": isMouseSupported() }}
w="$full"
gap="$1"
Expand Down
1 change: 1 addition & 0 deletions src/pages/home/folder/GridItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export const GridItem = (props: { obj: StoreObj; index: number }) => {
}}
>
<VStack
classList={{ selected: !!props.obj.selected }}
class="grid-item viselect-item"
data-index={props.index}
w="$full"
Expand Down
1 change: 1 addition & 0 deletions src/pages/home/folder/ImageItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export const ImageItem = (props: { obj: StoreObj; index: number }) => {
>
<VStack
w="$full"
classList={{ selected: !!props.obj.selected }}
class="image-item viselect-item"
data-index={props.index}
p="$1"
Expand Down
4 changes: 3 additions & 1 deletion src/pages/home/folder/Images.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@ const ImageLayout = (props: { images: StoreObj[] }) => {
</For>
</Grid>
))
const { isMouseSupported, registerSelectContainer } = useSelectWithMouse()
const { isMouseSupported, registerSelectContainer, captureContentMenu } =
useSelectWithMouse()
registerSelectContainer()
return (
<VStack
oncapture:contextmenu={captureContentMenu}
classList={{ "viselect-container": isMouseSupported() }}
spacing="$2"
w="$full"
Expand Down
4 changes: 3 additions & 1 deletion src/pages/home/folder/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,12 @@ const ListLayout = () => {
},
}
}
const { isMouseSupported, registerSelectContainer } = useSelectWithMouse()
const { isMouseSupported, registerSelectContainer, captureContentMenu } =
useSelectWithMouse()
registerSelectContainer()
return (
<VStack
oncapture:contextmenu={captureContentMenu}
classList={{ "viselect-container": isMouseSupported() }}
class="list"
w="$full"
Expand Down
1 change: 1 addition & 0 deletions src/pages/home/folder/ListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export const ListItem = (props: { obj: StoreObj; index: number }) => {
}}
>
<HStack
classList={{ selected: !!props.obj.selected }}
class="list-item viselect-item"
data-index={props.index}
w="$full"
Expand Down
157 changes: 96 additions & 61 deletions src/pages/home/folder/context-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import { useCopyLink, useDownload, useLink, useT } from "~/hooks"
import "solid-contextmenu/dist/style.css"
import { HStack, Icon, Text, useColorMode, Image } from "@hope-ui/solid"
import { operations } from "../toolbar/operations"
import { For } from "solid-js"
import { For, Show } from "solid-js"
import { bus, convertURL, notify } from "~/utils"
import { ObjType, UserMethods, UserPermissions } from "~/types"
import { getSettingBool, me } from "~/store"
import { getSettingBool, haveSelected, me, oneChecked } from "~/store"
import { players } from "../previews/video_box"
import { BsPlayCircleFill } from "solid-icons/bs"

Expand All @@ -29,7 +29,7 @@ export const ContextMenu = () => {
const t = useT()
const { colorMode } = useColorMode()
const { copySelectedRawLink, copySelectedPreviewPage } = useCopyLink()
const { batchDownloadSelected } = useDownload()
const { batchDownloadSelected, sendToAria2 } = useDownload()
const canPackageDownload = () => {
return UserMethods.is_admin(me()) || getSettingBool("package_download")
}
Expand All @@ -55,67 +55,102 @@ export const ContextMenu = () => {
</Item>
)}
</For>
<Item
onClick={({ props }) => {
if (props.is_dir) {
copySelectedPreviewPage()
} else {
copySelectedRawLink(true)
}
}}
>
<ItemContent name="copy_link" />
</Item>
<Item
onClick={({ props }) => {
if (props.is_dir) {
if (!canPackageDownload()) {
notify.warning(t("home.toolbar.package_download_disabled"))
return
<Show when={oneChecked()}>
<Item
onClick={({ props }) => {
if (props.is_dir) {
copySelectedPreviewPage()
} else {
copySelectedRawLink(true)
}
}}
>
<ItemContent name="copy_link" />
</Item>
<Item
onClick={({ props }) => {
if (props.is_dir) {
if (!canPackageDownload()) {
notify.warning(t("home.toolbar.package_download_disabled"))
return
}
bus.emit("tool", "package_download")
} else {
batchDownloadSelected()
}
bus.emit("tool", "package_download")
} else {
batchDownloadSelected()
}}
>
<ItemContent name="download" />
</Item>
<Submenu
hidden={({ props }) => {
return props.type !== ObjType.VIDEO
}}
label={
<HStack spacing="$2">
<Icon
as={BsPlayCircleFill}
boxSize="$7"
p="$0_5"
color="$info9"
/>
<Text>{t("home.preview.play_with")}</Text>
</HStack>
}
}}
>
<ItemContent name="download" />
</Item>
<Submenu
hidden={({ props }) => {
return props.type !== ObjType.VIDEO
}}
label={
<HStack spacing="$2">
<Icon as={BsPlayCircleFill} boxSize="$7" p="$0_5" color="$info9" />
<Text>{t("home.preview.play_with")}</Text>
</HStack>
}
>
<For each={players}>
{(player) => (
<Item
onClick={({ props }) => {
const href = convertURL(player.scheme, {
raw_url: "",
name: props.name,
d_url: rawLink(props, true),
})
window.open(href, "_self")
}}
>
<HStack spacing="$2">
<Image
m="0 auto"
boxSize="$7"
src={`${window.__dynamic_base__}/images/${player.icon}.webp`}
/>
<Text>{player.name}</Text>
</HStack>
>
<For each={players}>
{(player) => (
<Item
onClick={({ props }) => {
const href = convertURL(player.scheme, {
raw_url: "",
name: props.name,
d_url: rawLink(props, true),
})
window.open(href, "_self")
}}
>
<HStack spacing="$2">
<Image
m="0 auto"
boxSize="$7"
src={`${window.__dynamic_base__}/images/${player.icon}.webp`}
/>
<Text>{player.name}</Text>
</HStack>
</Item>
)}
</For>
</Submenu>
</Show>
<Show when={!oneChecked() && haveSelected()}>
<Submenu label={<ItemContent name="copy_link" />}>
<Item onClick={copySelectedPreviewPage}>
{t("home.toolbar.preview_page")}
</Item>
<Item onClick={() => copySelectedRawLink()}>
{t("home.toolbar.down_link")}
</Item>
<Item onClick={() => copySelectedRawLink(true)}>
{t("home.toolbar.encode_down_link")}
</Item>
</Submenu>
<Submenu label={<ItemContent name="download" />}>
<Item onClick={batchDownloadSelected}>
{t("home.toolbar.batch_download")}
</Item>
<Show
when={
UserMethods.is_admin(me()) || getSettingBool("package_download")
}
>
<Item onClick={() => bus.emit("tool", "package_download")}>
{t("home.toolbar.package_download")}
</Item>
)}
</For>
</Submenu>
</Show>
<Item onClick={sendToAria2}>{t("home.toolbar.send_aria2")}</Item>
</Submenu>
</Show>
</Menu>
)
}
37 changes: 34 additions & 3 deletions src/pages/home/folder/helper.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
import { createKeyHold } from "@solid-primitives/keyboard"
import { createEffect, createSignal, onCleanup } from "solid-js"
import { createEffect, createSignal, on, onCleanup } from "solid-js"
import SelectionArea from "@viselect/vanilla"
import { checkboxOpen, local, selectAll, selectIndex } from "~/store"
import {
checkboxOpen,
haveSelected,
local,
objStore,
oneChecked,
selectAll,
selectIndex,
selectedObjs,
} from "~/store"
import { isMac, isMobile } from "~/utils/compatibility"
import { useContextMenu } from "solid-contextmenu"

export function useOpenItemWithCheckbox() {
const [shouldOpen, setShouldOpen] = createSignal(
Expand Down Expand Up @@ -48,6 +58,8 @@ export function useSelectWithMouse() {
})
selection.on("start", ({ event }) => {
const ev = event as MouseEvent
selection.clearSelection(true, true)
selection.select(".viselect-item.selected", true)
if (!ev.ctrlKey && !ev.metaKey) {
selectAll(false)
selection.clearSelection()
Expand All @@ -72,5 +84,24 @@ export function useSelectWithMouse() {
})
}

return { isMouseSupported, registerSelectContainer }
const { show } = useContextMenu({ id: 1 })

const captureContentMenu = (e: MouseEvent) => {
e.preventDefault()

if (haveSelected() && !oneChecked()) {
const $target = e.target as Element
const $selectedItem = $target.closest(".viselect-item")
const index = Number($selectedItem?.getAttribute("data-index"))

const isClickOnContainer = Number.isNaN(index)
const isClickOnSelectedItems = () => !!objStore.objs[index].selected
if (isClickOnContainer || !isClickOnSelectedItems()) return

e.stopPropagation()
show(e, { props: objStore.obj })
}
}

return { isMouseSupported, registerSelectContainer, captureContentMenu }
}

0 comments on commit ba8fa12

Please sign in to comment.