@geoffcox/sterling-svelte 0.0.15 → 0.0.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,11 @@
1
1
  declare namespace svelte.JSX {
2
- interface DOMAttributes<T> {
3
- onclick_outside?: (e: CustomEvent) => void;
4
- }
2
+ type ClickOutsideEventDetail = {
3
+ mouseEvent: MouseEvent;
4
+ };
5
+
6
+ type ClickOutsideEvent = CustomEvent<ClickOutsideEventDetail>;
7
+
8
+ interface DOMAttributes<T> {
9
+ onclick_outside?: (e: ClickOutsideEvent) => void;
10
+ }
5
11
  }
@@ -0,0 +1,102 @@
1
+ <script>import { v4 as uuid } from "uuid";
2
+ import { createEventDispatcher, getContext, setContext } from "svelte";
3
+ import { writable } from "svelte/store";
4
+ import Button from "./Button.svelte";
5
+ import Menu from "../containers/Menu.svelte";
6
+ import { menuItemContextKey } from "../containers/Menus.constants";
7
+ import { focusFirstChild, focusNextChild, focusPreviousChild } from "../containers/Menus.utils";
8
+ export let menuItemId;
9
+ export let open = false;
10
+ export let shape = "rounded";
11
+ export let variant = "regular";
12
+ const instanceId = uuid();
13
+ let reference;
14
+ $:
15
+ menuId = `${menuItemId}-menu-${instanceId}`;
16
+ $:
17
+ hasChildren = $$slots.items;
18
+ const children = writable([]);
19
+ const dispatch = createEventDispatcher();
20
+ const raiseClose = (menuItemId2) => {
21
+ dispatch("close", { menuItemId: menuItemId2 });
22
+ };
23
+ const raiseOpen = (menuItemId2) => {
24
+ dispatch("open", { menuItemId: menuItemId2 });
25
+ };
26
+ const raiseSelect = (menuItemId2) => {
27
+ dispatch("select", { menuItemId: menuItemId2 });
28
+ };
29
+ const onClick = () => {
30
+ open = !open;
31
+ if (open) {
32
+ setTimeout(() => focusFirstChild($children), 10);
33
+ }
34
+ };
35
+ setContext(menuItemContextKey, {
36
+ rootMenuItemId: menuItemId,
37
+ depth: 1,
38
+ register: (menuItem) => {
39
+ children.set([...$children, menuItem]);
40
+ },
41
+ unregister: (menuItem) => {
42
+ children.set($children.filter((x) => x.id !== menuItem.id));
43
+ },
44
+ closeMenu: (recursive) => {
45
+ open = false;
46
+ },
47
+ focusPrevious: (fromMenuItemId) => focusPreviousChild($children, fromMenuItemId),
48
+ focusNext: (fromMenuItemId) => focusNextChild($children, fromMenuItemId),
49
+ onOpen: raiseOpen,
50
+ onClose: raiseClose,
51
+ onSelect: raiseSelect
52
+ });
53
+ </script>
54
+
55
+ <!--
56
+ @component
57
+ A Button that displays a context menu when clicked.
58
+ -->
59
+ <Button
60
+ aria-controls={menuId}
61
+ aria-expanded={open}
62
+ aria-haspopup={hasChildren}
63
+ aria-owns={menuId}
64
+ data-menu-item-id={menuItemId}
65
+ data-root-menu-item-id={menuItemId}
66
+ {variant}
67
+ {shape}
68
+ on:blur
69
+ on:click
70
+ on:click={onClick}
71
+ on:dblclick
72
+ on:focus
73
+ on:focusin
74
+ on:focusout
75
+ on:keydown
76
+ on:keypress
77
+ on:keyup
78
+ on:mousedown
79
+ on:mouseenter
80
+ on:mouseleave
81
+ on:mousemove
82
+ on:mouseover
83
+ on:mouseout
84
+ on:mouseup
85
+ on:pointercancel
86
+ on:pointerdown
87
+ on:pointerenter
88
+ on:pointerleave
89
+ on:pointermove
90
+ on:pointerover
91
+ on:pointerout
92
+ on:pointerup
93
+ on:wheel
94
+ {...$$restProps}
95
+ >
96
+ <div bind:this={reference}>
97
+ <slot />
98
+ <Menu id={menuId} {reference} {open}>
99
+ <slot name="items" />
100
+ </Menu>
101
+ </div>
102
+ </Button>
@@ -0,0 +1,54 @@
1
+ import { SvelteComponentTyped } from "svelte";
2
+ import type { ButtonShape, ButtonVariant } from './Button.types';
3
+ declare const __propDef: {
4
+ props: {
5
+ [x: string]: any;
6
+ menuItemId: string;
7
+ open?: boolean | undefined;
8
+ shape?: ButtonShape | undefined;
9
+ variant?: ButtonVariant | undefined;
10
+ };
11
+ events: {
12
+ blur: FocusEvent;
13
+ click: MouseEvent;
14
+ dblclick: MouseEvent;
15
+ focus: FocusEvent;
16
+ focusin: FocusEvent;
17
+ focusout: FocusEvent;
18
+ keydown: KeyboardEvent;
19
+ keypress: KeyboardEvent;
20
+ keyup: KeyboardEvent;
21
+ mousedown: MouseEvent;
22
+ mouseenter: MouseEvent;
23
+ mouseleave: MouseEvent;
24
+ mousemove: MouseEvent;
25
+ mouseover: MouseEvent;
26
+ mouseout: MouseEvent;
27
+ mouseup: MouseEvent;
28
+ pointercancel: PointerEvent;
29
+ pointerdown: PointerEvent;
30
+ pointerenter: PointerEvent;
31
+ pointerleave: PointerEvent;
32
+ pointermove: PointerEvent;
33
+ pointerover: PointerEvent;
34
+ pointerout: PointerEvent;
35
+ pointerup: PointerEvent;
36
+ wheel: WheelEvent;
37
+ close: CustomEvent<any>;
38
+ open: CustomEvent<any>;
39
+ select: CustomEvent<any>;
40
+ } & {
41
+ [evt: string]: CustomEvent<any>;
42
+ };
43
+ slots: {
44
+ default: {};
45
+ items: {};
46
+ };
47
+ };
48
+ export type MenuButtonProps = typeof __propDef.props;
49
+ export type MenuButtonEvents = typeof __propDef.events;
50
+ export type MenuButtonSlots = typeof __propDef.slots;
51
+ /** A Button that displays a context menu when clicked. */
52
+ export default class MenuButton extends SvelteComponentTyped<MenuButtonProps, MenuButtonEvents, MenuButtonSlots> {
53
+ }
54
+ export {};
package/clickOutside.js CHANGED
@@ -2,7 +2,9 @@ export const clickOutside = (node) => {
2
2
  const handleClick = (event) => {
3
3
  const targetNode = event?.target;
4
4
  if (targetNode && !node.contains(targetNode)) {
5
- node.dispatchEvent(new CustomEvent('click_outside'));
5
+ node.dispatchEvent(new CustomEvent('click_outside', {
6
+ detail: { mouseEvent: event }
7
+ }));
6
8
  }
7
9
  };
8
10
  document.addEventListener('click', handleClick, true);
@@ -122,7 +122,6 @@ A list of items where a single item can be selected.
122
122
  on:focus
123
123
  on:focusin
124
124
  on:focusout
125
- on:keydown={onKeydown}
126
125
  on:keydown
127
126
  on:keypress
128
127
  on:keyup
@@ -136,6 +135,7 @@ A list of items where a single item can be selected.
136
135
  on:scroll
137
136
  on:wheel
138
137
  on:paste
138
+ on:keydown={onKeydown}
139
139
  {...$$restProps}
140
140
  >
141
141
  {#each items as item, index (item)}
@@ -0,0 +1,100 @@
1
+ <script>import { getContext } from "svelte";
2
+ import { autoUpdate, computePosition, flip, offset, shift } from "@floating-ui/dom";
3
+ import { portal } from "../portal";
4
+ import { menuItemContextKey } from "./Menus.constants";
5
+ export let reference;
6
+ export let open = false;
7
+ let menuRef;
8
+ let menuPosition = { x: 0, y: 0 };
9
+ const { rootMenuItemId, depth = 0 } = getContext(menuItemContextKey) || {};
10
+ const ensurePortalHost = () => {
11
+ let host = document.querySelector("#SterlingMenuPortal");
12
+ if (!host) {
13
+ host = document.createElement("div");
14
+ host.id = "SterlingMenuPortal";
15
+ host.style.overflow = "visible";
16
+ document.body.append(host);
17
+ }
18
+ return host;
19
+ };
20
+ const portalTarget = ensurePortalHost();
21
+ const menuPlacement = depth > 1 ? "right-start" : "bottom-start";
22
+ const middleware = [offset({ mainAxis: -2 }), flip(), shift({ padding: 0 })];
23
+ const computeMenuPosition = async () => {
24
+ if (reference && menuRef) {
25
+ menuPosition = await computePosition(reference, menuRef, {
26
+ placement: menuPlacement,
27
+ middleware
28
+ });
29
+ } else {
30
+ menuPosition = { x: 0, y: 0 };
31
+ }
32
+ };
33
+ let cleanupAutoUpdate = () => {
34
+ };
35
+ const autoUpdateMenuPosition = () => {
36
+ cleanupAutoUpdate();
37
+ if (reference && menuRef) {
38
+ cleanupAutoUpdate = autoUpdate(reference, menuRef, computeMenuPosition);
39
+ }
40
+ };
41
+ $:
42
+ open, reference, menuRef, autoUpdateMenuPosition();
43
+ </script>
44
+
45
+ {#if open}
46
+ <div class="portal" data-root-menu-id={rootMenuItemId} use:portal={{ target: portalTarget }}>
47
+ <div
48
+ bind:this={menuRef}
49
+ class="menu"
50
+ class:open
51
+ {...$$restProps}
52
+ style="left:{menuPosition.x}px; top:{menuPosition.y}px"
53
+ >
54
+ {#if $$slots.default}
55
+ <!-- TODO: Remove this extra children div. Probably not needed -->
56
+ <div class="children" role="menu">
57
+ <slot />
58
+ </div>
59
+ {/if}
60
+ </div>
61
+ </div>
62
+ {/if}
63
+
64
+ <style>
65
+ .portal {
66
+ position: relative;
67
+ overflow: visible;
68
+ }
69
+
70
+ .menu {
71
+ background-color: var(--Common__background-color);
72
+ border-color: var(--Common__border-color);
73
+ border-radius: var(--Common__border-radius);
74
+ border-style: var(--Common__border-style);
75
+ border-width: var(--Common__border-width);
76
+ box-sizing: border-box;
77
+ position: absolute;
78
+ box-shadow: rgba(0, 0, 0, 0.4) 2px 2px 4px -1px;
79
+ overflow: hidden;
80
+ width: max-content;
81
+ height: fit-content;
82
+ z-index: 1;
83
+ top: 0;
84
+ left: 0;
85
+ }
86
+
87
+ .menu.open {
88
+ display: grid;
89
+ grid-template-columns: 1fr;
90
+ grid-template-rows: 1fr;
91
+ }
92
+
93
+ .children {
94
+ display: grid;
95
+ grid-template-columns: 1fr;
96
+ grid-template-rows: auto;
97
+ row-gap: calc(2 * var(--Common__outline-width));
98
+ padding: 0.25em;
99
+ }
100
+ </style>
@@ -0,0 +1,20 @@
1
+ import { SvelteComponentTyped } from "svelte";
2
+ declare const __propDef: {
3
+ props: {
4
+ [x: string]: any;
5
+ reference: HTMLElement;
6
+ open?: boolean | undefined;
7
+ };
8
+ events: {
9
+ [evt: string]: CustomEvent<any>;
10
+ };
11
+ slots: {
12
+ default: {};
13
+ };
14
+ };
15
+ export type MenuProps = typeof __propDef.props;
16
+ export type MenuEvents = typeof __propDef.events;
17
+ export type MenuSlots = typeof __propDef.slots;
18
+ export default class Menu extends SvelteComponentTyped<MenuProps, MenuEvents, MenuSlots> {
19
+ }
20
+ export {};
@@ -0,0 +1,97 @@
1
+ <script>import { createEventDispatcher, setContext } from "svelte";
2
+ import { menuBarContextKey, menuItemContextKey } from "./Menus.constants";
3
+ import { writable } from "svelte/store";
4
+ const dispatch = createEventDispatcher();
5
+ const raiseClose = (menuItemId) => {
6
+ dispatch("close", { menuItemId });
7
+ };
8
+ const raiseOpen = (menuItemId) => {
9
+ dispatch("open", { menuItemId });
10
+ };
11
+ const raiseSelect = (menuItemId) => {
12
+ dispatch("select", { menuItemId });
13
+ };
14
+ const children = writable([]);
15
+ const openPreviousChild = (fromMenuItemId) => {
16
+ const index = $children?.findIndex((menuItem) => menuItem.id === fromMenuItemId);
17
+ if (index !== -1) {
18
+ const focusIndex = index === 0 ? $children.length - 1 : index - 1;
19
+ $children[focusIndex].focus();
20
+ $children[focusIndex].open();
21
+ }
22
+ };
23
+ const openNextChild = (fromMenuItemId) => {
24
+ const index = $children?.findIndex((menuItem) => menuItem.id === fromMenuItemId);
25
+ if (index !== -1) {
26
+ const focusIndex = (index + 1) % $children.length;
27
+ $children[focusIndex].focus();
28
+ $children[focusIndex].open();
29
+ }
30
+ };
31
+ const focusChild = (menuItemId) => {
32
+ const focusIndex = $children?.findIndex((menuItem) => menuItem.id === menuItemId);
33
+ if (focusIndex !== -1) {
34
+ $children[focusIndex].focus();
35
+ }
36
+ };
37
+ setContext(menuItemContextKey, {
38
+ register: (menuItem) => {
39
+ children.set([...$children, menuItem]);
40
+ },
41
+ unregister: (menuItem) => {
42
+ children.set($children.filter((x) => x.id !== menuItem.id));
43
+ },
44
+ closeMenu: (recursive) => {
45
+ },
46
+ focusPrevious: openPreviousChild,
47
+ focusNext: openNextChild,
48
+ onClose: raiseClose,
49
+ onOpen: raiseOpen,
50
+ onSelect: raiseSelect
51
+ });
52
+ setContext(menuBarContextKey, {
53
+ openPreviousMenu: openPreviousChild,
54
+ openNextMenu: openNextChild
55
+ });
56
+ </script>
57
+
58
+ <div
59
+ class="sterling-menu-bar"
60
+ role="menubar"
61
+ on:blur
62
+ on:click
63
+ on:copy
64
+ on:cut
65
+ on:dblclick
66
+ on:focus
67
+ on:focusin
68
+ on:focusout
69
+ on:keydown
70
+ on:keypress
71
+ on:keyup
72
+ on:mousedown
73
+ on:mouseenter
74
+ on:mouseleave
75
+ on:mousemove
76
+ on:mouseover
77
+ on:mouseout
78
+ on:mouseup
79
+ on:scroll
80
+ on:wheel
81
+ on:paste
82
+ {...$$restProps}
83
+ >
84
+ <slot />
85
+ </div>
86
+
87
+ <style>
88
+ .sterling-menu-bar {
89
+ background-color: transparent;
90
+ align-items: center;
91
+ align-content: stretch;
92
+ display: flex;
93
+ gap: 1em;
94
+ justify-content: flex-start;
95
+ justify-items: flex-start;
96
+ }
97
+ </style>
@@ -0,0 +1,43 @@
1
+ import { SvelteComponentTyped } from "svelte";
2
+ declare const __propDef: {
3
+ props: {
4
+ [x: string]: never;
5
+ };
6
+ events: {
7
+ blur: FocusEvent;
8
+ click: MouseEvent;
9
+ copy: ClipboardEvent;
10
+ cut: ClipboardEvent;
11
+ dblclick: MouseEvent;
12
+ focus: FocusEvent;
13
+ focusin: FocusEvent;
14
+ focusout: FocusEvent;
15
+ keydown: KeyboardEvent;
16
+ keypress: KeyboardEvent;
17
+ keyup: KeyboardEvent;
18
+ mousedown: MouseEvent;
19
+ mouseenter: MouseEvent;
20
+ mouseleave: MouseEvent;
21
+ mousemove: MouseEvent;
22
+ mouseover: MouseEvent;
23
+ mouseout: MouseEvent;
24
+ mouseup: MouseEvent;
25
+ scroll: Event;
26
+ wheel: WheelEvent;
27
+ paste: ClipboardEvent;
28
+ close: CustomEvent<any>;
29
+ open: CustomEvent<any>;
30
+ select: CustomEvent<any>;
31
+ } & {
32
+ [evt: string]: CustomEvent<any>;
33
+ };
34
+ slots: {
35
+ default: {};
36
+ };
37
+ };
38
+ export type MenuBarProps = typeof __propDef.props;
39
+ export type MenuBarEvents = typeof __propDef.events;
40
+ export type MenuBarSlots = typeof __propDef.slots;
41
+ export default class MenuBar extends SvelteComponentTyped<MenuBarProps, MenuBarEvents, MenuBarSlots> {
42
+ }
43
+ export {};