diff --git a/package.json b/package.json index a7ac8b0..c0c591c 100644 --- a/package.json +++ b/package.json @@ -15,11 +15,13 @@ "typesafe-i18n": "tsx scripts/import_translations.ts" }, "dependencies": { + "@floating-ui/core": "^1.6.0", "@fontsource-variable/inter": "^5.0.16", "@mdi/js": "^7.4.47", "@resreq/event-hub": "^1.6.0", "daisyui": "^4.7.2", "fast-average-color": "^9.4.0", + "svelte-floating-ui": "^1.5.8", "svelte-inview": "^4.0.2", "svelte-local-storage-store": "^0.6.4", "typesafe-i18n": "^5.26.2" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bb98168..2a7cdf3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,6 +5,9 @@ settings: excludeLinksFromLockfile: false dependencies: + '@floating-ui/core': + specifier: ^1.6.0 + version: 1.6.0 '@fontsource-variable/inter': specifier: ^5.0.16 version: 5.0.16 @@ -20,6 +23,9 @@ dependencies: fast-average-color: specifier: ^9.4.0 version: 9.4.0 + svelte-floating-ui: + specifier: ^1.5.8 + version: 1.5.8 svelte-inview: specifier: ^4.0.2 version: 4.0.2(svelte@4.2.11) @@ -392,18 +398,15 @@ packages: resolution: {integrity: sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==} dependencies: '@floating-ui/utils': 0.2.1 - dev: true /@floating-ui/dom@1.6.3: resolution: {integrity: sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw==} dependencies: '@floating-ui/core': 1.6.0 '@floating-ui/utils': 0.2.1 - dev: true /@floating-ui/utils@0.2.1: resolution: {integrity: sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==} - dev: true /@fontsource-variable/inter@5.0.16: resolution: {integrity: sha512-k+BUNqksTL+AN+o+OV7ILeiE9B5M5X+/jA7LWvCwjbV9ovXTqZyKRhA/x7uYv/ml8WQ0XNLBM7cRFIx4jW0/hg==} @@ -2892,6 +2895,13 @@ packages: svelte: 4.2.11 dev: true + /svelte-floating-ui@1.5.8: + resolution: {integrity: sha512-dVvJhZ2bT+kQDHlE4Lep8t+sgEc0XD96fXLzAi2DDI2bsaegBbClxXVNMma0C2WsG+n9GJSYx292dTvA8CYRtw==} + dependencies: + '@floating-ui/core': 1.6.0 + '@floating-ui/dom': 1.6.3 + dev: false + /svelte-hmr@0.15.3(svelte@4.2.11): resolution: {integrity: sha512-41snaPswvSf8TJUhlkoJBekRrABDXDMdpNpT2tfHIv4JuhgvHqLMhEPGtaQn0BmbNSTkuz2Ed20DF2eHw0SmBQ==} engines: {node: ^12.20 || ^14.13.1 || >= 16} diff --git a/src/lib/components/contextmenu/BottomSheet.svelte b/src/lib/components/contextmenu/BottomSheet.svelte new file mode 100644 index 0000000..bf2f868 --- /dev/null +++ b/src/lib/components/contextmenu/BottomSheet.svelte @@ -0,0 +1,150 @@ + + +
$screenHeight * 0.1 }} + on:swipeStart={onSwipeStart} + on:swipeMove={onSwipeMove} + on:swipeEnd={onSwipeEnd} + on:swipeFailed={reset} +> +
+ +
+ + diff --git a/src/lib/components/contextmenu/TestCtxMenu2.svelte b/src/lib/components/contextmenu/TestCtxMenu2.svelte new file mode 100644 index 0000000..46a6363 --- /dev/null +++ b/src/lib/components/contextmenu/TestCtxMenu2.svelte @@ -0,0 +1,134 @@ + + + { + if (e.key === kbd.ESCAPE) { + onOutclick(); + } + }} +/> + +{#if menuOpen} + +{/if} diff --git a/src/lib/components/contextmenu/TestCtxMenu2Item.svelte b/src/lib/components/contextmenu/TestCtxMenu2Item.svelte new file mode 100644 index 0000000..ced0d2d --- /dev/null +++ b/src/lib/components/contextmenu/TestCtxMenu2Item.svelte @@ -0,0 +1,114 @@ + + + diff --git a/src/lib/components/contextmenu/menus.ts b/src/lib/components/contextmenu/menus.ts new file mode 100644 index 0000000..cec0fcc --- /dev/null +++ b/src/lib/components/contextmenu/menus.ts @@ -0,0 +1,66 @@ +import type { TranslationFunctions } from "$i18n/i18n-types"; +import { + mdiContentCopy, + mdiDownload, + mdiPlaylistPlay, + mdiPlaylistPlus, + mdiShare, +} from "@mdi/js"; + +export type CtxMenuEntry = { + /** Item text (translation key) */ + title: keyof TranslationFunctions; + /** Item icon */ + icon?: string; + /** Item click action */ + onClick?: () => void; + /** Place a separator before the menu item */ + separator?: boolean; + /** Submenu entries */ + submenu?: CtxMenuEntry[]; + key?: string; +}; + +export const TEST_MENU: CtxMenuEntry[] = [ + { + title: "play_next", + icon: mdiPlaylistPlay, + onClick: () => console.log("play_next"), + key: "n", + }, + { + title: "add_to_queue", + icon: mdiPlaylistPlus, + key: "q", + }, + { + title: "share", + icon: mdiShare, + separator: true, + submenu: [ + { + title: "copy_url", + icon: mdiContentCopy, + }, + { + title: "download_audio_file", + icon: mdiDownload, + }, + { + title: "share", + icon: mdiShare, + separator: true, + submenu: [ + { + title: "copy_url", + icon: mdiContentCopy, + }, + { + title: "download_audio_file", + icon: mdiDownload, + }, + ], + } + ], + } +]; diff --git a/src/lib/components/header/ContentHeader.svelte b/src/lib/components/header/ContentHeader.svelte index d1c8f96..7692b5b 100644 --- a/src/lib/components/header/ContentHeader.svelte +++ b/src/lib/components/header/ContentHeader.svelte @@ -10,8 +10,8 @@ The header sticks to the top of the page when scrolling down. --> import type { Color } from "$lib/util/types"; import { COLOR_B3, + DROPDOWN_CFG, MIN_CONTRAST_TITLE, - POSITIONING_DROPDOWN, } from "$lib/util/constants"; import ImgFrame from "$lib/components/ui/ImgFrame.svelte"; @@ -50,7 +50,7 @@ The header sticks to the top of the page when scrolling down. --> elements: { trigger, menu, item }, builders: { createSubmenu }, states: { open: ddnOpen }, - } = createDropdownMenu({ positioning: POSITIONING_DROPDOWN }); + } = createDropdownMenu(DROPDOWN_CFG);
+ + +
diff --git a/src/style/app.css b/src/style/app.pcss similarity index 93% rename from src/style/app.css rename to src/style/app.pcss index b4f7f35..d421c22 100644 --- a/src/style/app.css +++ b/src/style/app.pcss @@ -76,3 +76,7 @@ mark { max-width: 300px; position: fixed; } + +.thin-line { + @apply border-base-content/20 border-solid border-[1px]; +}