summaryrefslogtreecommitdiffstats
path: root/apps/web-shared/src/components/menu
diff options
context:
space:
mode:
Diffstat (limited to 'apps/web-shared/src/components/menu')
-rw-r--r--apps/web-shared/src/components/menu/index.ts9
-rw-r--r--apps/web-shared/src/components/menu/item.svelte8
-rw-r--r--apps/web-shared/src/components/menu/menu.svelte54
-rw-r--r--apps/web-shared/src/components/menu/separator.svelte2
4 files changed, 73 insertions, 0 deletions
diff --git a/apps/web-shared/src/components/menu/index.ts b/apps/web-shared/src/components/menu/index.ts
new file mode 100644
index 0000000..8eb7938
--- /dev/null
+++ b/apps/web-shared/src/components/menu/index.ts
@@ -0,0 +1,9 @@
+import Menu from "./menu.svelte";
+import MenuItem from "./item.svelte";
+import MenuItemSeparator from "./separator.svelte";
+
+export {
+ Menu,
+ MenuItem,
+ MenuItemSeparator
+};
diff --git a/apps/web-shared/src/components/menu/item.svelte b/apps/web-shared/src/components/menu/item.svelte
new file mode 100644
index 0000000..aeb0f99
--- /dev/null
+++ b/apps/web-shared/src/components/menu/item.svelte
@@ -0,0 +1,8 @@
+<script lang="ts">
+ export let danger = false;
+</script>
+<li role="menuitem" on:click>
+ <span class="menu__content {danger ? 'bg-error-lighter@hover color-white@hover' : ''}">
+ <slot/>
+ </span>
+</li>
diff --git a/apps/web-shared/src/components/menu/menu.svelte b/apps/web-shared/src/components/menu/menu.svelte
new file mode 100644
index 0000000..33b1160
--- /dev/null
+++ b/apps/web-shared/src/components/menu/menu.svelte
@@ -0,0 +1,54 @@
+<script lang="ts">
+ import {random_string} from "$shared/lib/helpers";
+
+ export let id = "__menu_" + random_string(3);
+ export let trigger: HTMLElement;
+ export let show = false;
+
+ let windowInnerWidth = 0;
+ let windowInnerHeight = 0;
+ let menu: HTMLMenuElement;
+
+ $: if (show && menu && trigger) {
+ const
+ selectedTriggerPosition = trigger.getBoundingClientRect(),
+ menuOnTop = (windowInnerHeight - selectedTriggerPosition.bottom) < selectedTriggerPosition.top,
+ left = selectedTriggerPosition.left,
+ right = (windowInnerWidth - selectedTriggerPosition.right),
+ isRight = (windowInnerWidth < selectedTriggerPosition.left + menu.offsetWidth),
+ vertical = menuOnTop
+ ? "bottom: " + (windowInnerHeight - selectedTriggerPosition.top) + "px;"
+ : "top: " + selectedTriggerPosition.bottom + "px;";
+
+ let horizontal = isRight ? "right: " + right + "px;" : "left: " + left + "px;";
+
+ // check right position is correct -> otherwise set left to 0
+ if (isRight && (right + menu.offsetWidth) > windowInnerWidth) horizontal = ("left: " + (windowInnerWidth - menu.offsetWidth) / 2 + "px;");
+ const maxHeight = menuOnTop ? selectedTriggerPosition.top - 20 : windowInnerHeight - selectedTriggerPosition.bottom - 20;
+ menu.setAttribute("style", horizontal + vertical + "max-height:" + Math.floor(maxHeight) + "px;");
+ }
+
+ function on_window_click(event) {
+ if (!event.target.closest("#" + id) && !event.target.closest("[aria-controls='" + id + "']")) show = false;
+ }
+
+ function on_window_touchend(event) {
+ if (!event.target.closest("#" + id) && !event.target.closest("[aria-controls='" + id + "']")) show = false;
+ }
+</script>
+
+<svelte:window
+ on:click={on_window_click}
+ on:touchend={on_window_touchend}
+ bind:innerWidth={windowInnerWidth}
+ bind:innerHeight={windowInnerHeight}
+/>
+
+<menu class="menu"
+ id="{id}"
+ bind:this={menu}
+ class:menu--is-visible={show}
+ aria-expanded="{show}"
+ aria-haspopup="true">
+ <slot name="options"/>
+</menu>
diff --git a/apps/web-shared/src/components/menu/separator.svelte b/apps/web-shared/src/components/menu/separator.svelte
new file mode 100644
index 0000000..798dce0
--- /dev/null
+++ b/apps/web-shared/src/components/menu/separator.svelte
@@ -0,0 +1,2 @@
+<li class="menu__separator"
+ role="separator"></li>