@geoffcox/sterling-svelte 0.0.24 → 0.0.26
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.
- package/Dropdown.svelte +26 -39
- package/Field.svelte +3 -15
- package/List.svelte +3 -15
- package/Menu.svelte +86 -115
- package/Menu.svelte.d.ts +8 -2
- package/MenuBar.svelte +76 -31
- package/MenuBar.types.d.ts +2 -2
- package/MenuButton.svelte +50 -28
- package/MenuItem.constants.d.ts +1 -0
- package/MenuItem.constants.js +1 -0
- package/MenuItem.svelte +169 -128
- package/MenuItem.svelte.d.ts +6 -3
- package/MenuItem.types.d.ts +14 -5
- package/MenuItem.utils.d.ts +2 -0
- package/MenuItem.utils.js +16 -0
- package/MenuItemDisplay.svelte +2 -1
- package/MenuItemDisplay.svelte.d.ts +1 -0
- package/Popover.svelte +124 -0
- package/Popover.svelte.d.ts +51 -0
- package/Progress.svelte +13 -2
- package/Progress.svelte.d.ts +1 -0
- package/Radio.svelte +39 -22
- package/Select.svelte +17 -48
- package/Switch.svelte +7 -3
- package/Tab.svelte +1 -1
- package/TabList.svelte +2 -18
- package/TabList.types.d.ts +0 -1
- package/TextArea.svelte +10 -8
- package/TextArea.svelte.d.ts +1 -1
- package/Tree.svelte +3 -14
- package/TreeItem.svelte +7 -1
- package/actions/clickOutside.d.ts +12 -1
- package/actions/clickOutside.js +13 -2
- package/index.d.ts +4 -3
- package/index.js +3 -2
- package/package.json +4 -1
- package/stores/prefersReducedMotion.d.ts +1 -0
- package/stores/prefersReducedMotion.js +10 -0
- package/stores/usingKeyboard.d.ts +1 -0
- package/stores/usingKeyboard.js +13 -0
package/MenuButton.svelte
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
<script>import { createEventDispatcher,
|
|
1
|
+
<script>import { createEventDispatcher, setContext, tick } from "svelte";
|
|
2
2
|
import { writable } from "svelte/store";
|
|
3
3
|
import Button from "./Button.svelte";
|
|
4
4
|
import Menu from "./Menu.svelte";
|
|
5
5
|
import { MENU_ITEM_CONTEXT_KEY } from "./MenuItem.constants";
|
|
6
|
-
import { focusFirstChild, focusNextChild, focusPreviousChild } from "./MenuItem.utils";
|
|
7
6
|
import { idGenerator } from "./idGenerator";
|
|
7
|
+
import Popover from "./Popover.svelte";
|
|
8
|
+
import { clickOutside } from "./actions/clickOutside";
|
|
9
|
+
import { usingKeyboard } from "./stores/usingKeyboard";
|
|
8
10
|
export let open = false;
|
|
9
11
|
export let shape = "rounded";
|
|
10
12
|
export let value;
|
|
@@ -12,12 +14,16 @@ export let variant = "regular";
|
|
|
12
14
|
const instanceId = idGenerator.nextId("MenuButton");
|
|
13
15
|
let buttonRef;
|
|
14
16
|
let reference;
|
|
17
|
+
let menuRef;
|
|
15
18
|
let prevOpen = open;
|
|
16
19
|
$:
|
|
17
20
|
menuId = `${value}-menu-${instanceId}`;
|
|
18
21
|
$:
|
|
19
22
|
hasChildren = $$slots.items;
|
|
20
|
-
const
|
|
23
|
+
const openValues = writable([]);
|
|
24
|
+
$: {
|
|
25
|
+
open = $openValues.length > 0;
|
|
26
|
+
}
|
|
21
27
|
const dispatch = createEventDispatcher();
|
|
22
28
|
const raiseClose = (value2) => {
|
|
23
29
|
dispatch("close", { value: value2 });
|
|
@@ -28,10 +34,25 @@ const raiseOpen = (value2) => {
|
|
|
28
34
|
const raiseSelect = (value2) => {
|
|
29
35
|
dispatch("select", { value: value2 });
|
|
30
36
|
};
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
37
|
+
export const click = () => {
|
|
38
|
+
buttonRef?.click();
|
|
39
|
+
};
|
|
40
|
+
export const blur = () => {
|
|
41
|
+
buttonRef?.blur();
|
|
42
|
+
};
|
|
43
|
+
export const focus = (options) => {
|
|
44
|
+
buttonRef?.focus(options);
|
|
45
|
+
};
|
|
46
|
+
const onClick = async () => {
|
|
47
|
+
if (!open) {
|
|
48
|
+
openValues.set(["menu-button"]);
|
|
49
|
+
if ($usingKeyboard) {
|
|
50
|
+
await tick();
|
|
51
|
+
menuRef?.focusFirstMenuItem();
|
|
52
|
+
}
|
|
53
|
+
} else {
|
|
54
|
+
open = false;
|
|
55
|
+
openValues.set([]);
|
|
35
56
|
}
|
|
36
57
|
};
|
|
37
58
|
$: {
|
|
@@ -40,29 +61,28 @@ $: {
|
|
|
40
61
|
}
|
|
41
62
|
prevOpen = open;
|
|
42
63
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
};
|
|
46
|
-
export const blur = () => {
|
|
47
|
-
buttonRef?.blur();
|
|
64
|
+
const closeAllMenus = () => {
|
|
65
|
+
openValues.set([]);
|
|
48
66
|
};
|
|
49
|
-
|
|
50
|
-
|
|
67
|
+
const onClickOutside = (event) => {
|
|
68
|
+
const {
|
|
69
|
+
detail: { mouseEvent }
|
|
70
|
+
} = event;
|
|
71
|
+
let element = mouseEvent.target;
|
|
72
|
+
while (element) {
|
|
73
|
+
if (element.getAttribute("data-root-value") === value) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
element = element.parentElement;
|
|
77
|
+
}
|
|
78
|
+
closeAllMenus?.();
|
|
51
79
|
};
|
|
52
80
|
setContext(MENU_ITEM_CONTEXT_KEY, {
|
|
81
|
+
openValues,
|
|
53
82
|
rootValue: value,
|
|
54
|
-
|
|
55
|
-
register: (menuItem) => {
|
|
56
|
-
children.set([...$children, menuItem]);
|
|
57
|
-
},
|
|
58
|
-
unregister: (menuItem) => {
|
|
59
|
-
children.set($children.filter((x) => x.value !== menuItem.value));
|
|
60
|
-
},
|
|
61
|
-
closeMenu: (recursive) => {
|
|
83
|
+
closeContainingMenu: () => {
|
|
62
84
|
open = false;
|
|
63
85
|
},
|
|
64
|
-
focusPrevious: (currentValue) => focusPreviousChild($children, currentValue),
|
|
65
|
-
focusNext: (currentValue) => focusNextChild($children, currentValue),
|
|
66
86
|
onOpen: raiseOpen,
|
|
67
87
|
onClose: raiseClose,
|
|
68
88
|
onSelect: raiseSelect
|
|
@@ -117,12 +137,14 @@ setContext(MENU_ITEM_CONTEXT_KEY, {
|
|
|
117
137
|
on:wheel
|
|
118
138
|
{...$$restProps}
|
|
119
139
|
>
|
|
120
|
-
<div class="reference" bind:this={reference}>
|
|
140
|
+
<div class="reference" bind:this={reference} use:clickOutside on:click_outside={onClickOutside}>
|
|
121
141
|
<slot {shape} {variant} />
|
|
122
142
|
</div>
|
|
123
|
-
<
|
|
124
|
-
<
|
|
125
|
-
|
|
143
|
+
<Popover {reference} placement="bottom-start" {open}>
|
|
144
|
+
<Menu bind:this={menuRef} id={menuId} {reference} {open}>
|
|
145
|
+
<slot name="items" />
|
|
146
|
+
</Menu>
|
|
147
|
+
</Popover>
|
|
126
148
|
</Button>
|
|
127
149
|
|
|
128
150
|
<style>
|
package/MenuItem.constants.d.ts
CHANGED
package/MenuItem.constants.js
CHANGED
package/MenuItem.svelte
CHANGED
|
@@ -1,50 +1,60 @@
|
|
|
1
|
-
<script>import {
|
|
2
|
-
|
|
1
|
+
<script>import {
|
|
2
|
+
getContext,
|
|
3
|
+
afterUpdate,
|
|
4
|
+
createEventDispatcher,
|
|
5
|
+
onMount,
|
|
6
|
+
setContext,
|
|
7
|
+
tick
|
|
8
|
+
} from "svelte";
|
|
3
9
|
import { writable } from "svelte/store";
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
import MenuItemDisplay from "./MenuItemDisplay.svelte";
|
|
10
|
+
import { idGenerator } from "./idGenerator";
|
|
11
|
+
import Menu from "./Menu.svelte";
|
|
7
12
|
import { MENU_BAR_CONTEXT_KEY } from "./MenuBar.constants";
|
|
8
13
|
import { MENU_ITEM_CONTEXT_KEY } from "./MenuItem.constants";
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
focusNextChild,
|
|
14
|
-
focusPreviousChild
|
|
15
|
-
} from "./MenuItem.utils";
|
|
16
|
-
import { idGenerator } from "./idGenerator";
|
|
14
|
+
import { isElementEnabledMenuItem } from "./MenuItem.utils";
|
|
15
|
+
import MenuItemDisplay from "./MenuItemDisplay.svelte";
|
|
16
|
+
import Popover from "./Popover.svelte";
|
|
17
|
+
import { usingKeyboard } from "./stores/usingKeyboard";
|
|
17
18
|
export let checked = false;
|
|
18
19
|
export let composed = false;
|
|
19
20
|
export let disabled = false;
|
|
20
|
-
export let open = false;
|
|
21
21
|
export let value;
|
|
22
22
|
export let role = "menuitem";
|
|
23
23
|
export let text = void 0;
|
|
24
24
|
const {
|
|
25
|
+
isMenuBarItem,
|
|
26
|
+
openValues = writable([]),
|
|
25
27
|
rootValue = value,
|
|
26
28
|
depth = 0,
|
|
27
|
-
|
|
28
|
-
unregister = void 0,
|
|
29
|
-
closeMenu = void 0,
|
|
30
|
-
focusPrevious = void 0,
|
|
31
|
-
focusNext = void 0,
|
|
29
|
+
closeContainingMenu = void 0,
|
|
32
30
|
onOpen = void 0,
|
|
33
31
|
onClose = void 0,
|
|
34
32
|
onSelect = void 0
|
|
35
33
|
} = getContext(MENU_ITEM_CONTEXT_KEY) || {};
|
|
36
|
-
const {
|
|
34
|
+
const { openPreviousMenuBarItem = void 0, openNextMenuBarItem = void 0 } = getContext(MENU_BAR_CONTEXT_KEY) || {};
|
|
37
35
|
const instanceId = idGenerator.nextId("MenuItem");
|
|
38
36
|
$:
|
|
39
37
|
displayId = `${value}-display-${instanceId}`;
|
|
38
|
+
$:
|
|
39
|
+
open = $openValues.includes(value);
|
|
40
40
|
$:
|
|
41
41
|
menuId = `${value}-menu-${instanceId}`;
|
|
42
42
|
let menuItemRef;
|
|
43
|
+
let menuRef;
|
|
43
44
|
const children = writable([]);
|
|
44
45
|
let mounted = false;
|
|
45
46
|
let prevOpen = open;
|
|
46
47
|
$:
|
|
47
48
|
hasChildren = $$slots.default;
|
|
49
|
+
export const blur = () => {
|
|
50
|
+
menuItemRef?.blur();
|
|
51
|
+
};
|
|
52
|
+
export const click = () => {
|
|
53
|
+
menuItemRef?.click();
|
|
54
|
+
};
|
|
55
|
+
export const focus = (options) => {
|
|
56
|
+
menuItemRef?.focus(options);
|
|
57
|
+
};
|
|
48
58
|
const dispatch = createEventDispatcher();
|
|
49
59
|
const raiseClose = (value2) => {
|
|
50
60
|
dispatch("close", { value: value2 });
|
|
@@ -64,157 +74,176 @@ const raiseSelect = (value2) => {
|
|
|
64
74
|
dispatch("select", { value: value2 });
|
|
65
75
|
onSelect?.(value2);
|
|
66
76
|
};
|
|
67
|
-
|
|
68
|
-
let
|
|
69
|
-
|
|
70
|
-
|
|
77
|
+
const focusPreviousMenuItem = () => {
|
|
78
|
+
let candidate = menuItemRef?.previousElementSibling || menuItemRef?.parentElement?.lastElementChild;
|
|
79
|
+
while (candidate && !isElementEnabledMenuItem(candidate)) {
|
|
80
|
+
candidate = candidate.previousElementSibling || menuItemRef?.parentElement?.lastElementChild;
|
|
81
|
+
if (candidate === menuItemRef) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
candidate?.focus();
|
|
86
|
+
return !!candidate;
|
|
71
87
|
};
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
88
|
+
const focusNextMenuItem = () => {
|
|
89
|
+
let candidate = menuItemRef?.nextElementSibling || menuItemRef?.parentElement?.firstElementChild;
|
|
90
|
+
while (candidate && !isElementEnabledMenuItem(candidate)) {
|
|
91
|
+
candidate = candidate.nextElementSibling || menuItemRef?.parentElement?.firstElementChild;
|
|
92
|
+
if (candidate === menuItemRef) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
75
95
|
}
|
|
96
|
+
candidate?.focus();
|
|
97
|
+
return !!candidate;
|
|
76
98
|
};
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
99
|
+
const openMenu = () => {
|
|
100
|
+
if (!$openValues.includes(value)) {
|
|
101
|
+
openValues.set([...$openValues.slice(0, depth), value]);
|
|
102
|
+
}
|
|
81
103
|
};
|
|
82
|
-
|
|
83
|
-
|
|
104
|
+
const closeMenu = async () => {
|
|
105
|
+
const index = $openValues.indexOf(value);
|
|
106
|
+
if (index !== -1) {
|
|
107
|
+
openValues.set([...$openValues.slice(0, index)]);
|
|
108
|
+
await tick();
|
|
109
|
+
menuItemRef?.focus();
|
|
110
|
+
}
|
|
84
111
|
};
|
|
85
|
-
|
|
86
|
-
|
|
112
|
+
const closeAllMenus = () => {
|
|
113
|
+
openValues.set([]);
|
|
87
114
|
};
|
|
88
115
|
onMount(() => {
|
|
89
116
|
mounted = true;
|
|
90
|
-
keyborg.subscribe(keyborgHandler);
|
|
91
|
-
const menuItemSelf = {
|
|
92
|
-
value,
|
|
93
|
-
open: () => {
|
|
94
|
-
open = true;
|
|
95
|
-
},
|
|
96
|
-
close: () => {
|
|
97
|
-
open = false;
|
|
98
|
-
},
|
|
99
|
-
focus: () => {
|
|
100
|
-
menuItemRef?.focus();
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
register?.(menuItemSelf);
|
|
104
|
-
return () => {
|
|
105
|
-
keyborg.unsubscribe(keyborgHandler);
|
|
106
|
-
unregister?.(menuItemSelf);
|
|
107
|
-
};
|
|
108
117
|
});
|
|
109
118
|
afterUpdate(() => {
|
|
110
119
|
prevOpen = open;
|
|
111
120
|
});
|
|
112
|
-
const onKeyDown = (event) => {
|
|
121
|
+
const onKeyDown = async (event) => {
|
|
113
122
|
if (!disabled && !event.altKey && !event.ctrlKey && !event.shiftKey) {
|
|
114
123
|
switch (event.key) {
|
|
115
124
|
case "ArrowDown":
|
|
116
|
-
if (
|
|
117
|
-
|
|
118
|
-
setTimeout(() =>
|
|
125
|
+
if (isMenuBarItem && hasChildren) {
|
|
126
|
+
openMenu();
|
|
127
|
+
setTimeout(async () => {
|
|
128
|
+
await tick();
|
|
129
|
+
menuRef?.focusFirstMenuItem();
|
|
130
|
+
}, 10);
|
|
119
131
|
event.preventDefault();
|
|
132
|
+
event.stopPropagation();
|
|
120
133
|
return false;
|
|
121
|
-
}
|
|
122
|
-
|
|
134
|
+
}
|
|
135
|
+
if (!isMenuBarItem) {
|
|
136
|
+
focusNextMenuItem();
|
|
123
137
|
event.preventDefault();
|
|
138
|
+
event.stopPropagation();
|
|
124
139
|
return false;
|
|
125
140
|
}
|
|
126
141
|
break;
|
|
127
142
|
case "ArrowLeft":
|
|
128
|
-
if (
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
143
|
+
if (isMenuBarItem) {
|
|
144
|
+
focusPreviousMenuItem();
|
|
145
|
+
event.preventDefault();
|
|
146
|
+
event.stopPropagation();
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
if (depth > 1) {
|
|
150
|
+
closeContainingMenu?.();
|
|
151
|
+
event.preventDefault();
|
|
152
|
+
event.stopPropagation();
|
|
153
|
+
return false;
|
|
134
154
|
}
|
|
155
|
+
openPreviousMenuBarItem?.();
|
|
135
156
|
event.preventDefault();
|
|
157
|
+
event.stopPropagation();
|
|
136
158
|
return false;
|
|
137
159
|
case "ArrowRight":
|
|
138
|
-
if (
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
closeMenu?.(true);
|
|
144
|
-
openNextMenu?.(rootValue);
|
|
160
|
+
if (isMenuBarItem) {
|
|
161
|
+
focusNextMenuItem();
|
|
162
|
+
event.preventDefault();
|
|
163
|
+
event.stopPropagation();
|
|
164
|
+
return false;
|
|
145
165
|
}
|
|
146
|
-
|
|
147
|
-
|
|
166
|
+
if (hasChildren) {
|
|
167
|
+
openMenu();
|
|
168
|
+
setTimeout(async () => {
|
|
169
|
+
await tick();
|
|
170
|
+
menuRef?.focusFirstMenuItem();
|
|
171
|
+
}, 10);
|
|
172
|
+
event.preventDefault();
|
|
173
|
+
event.stopPropagation();
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
if (openNextMenuBarItem) {
|
|
177
|
+
openNextMenuBarItem();
|
|
178
|
+
event.preventDefault();
|
|
179
|
+
event.stopPropagation();
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
break;
|
|
148
183
|
case "ArrowUp":
|
|
149
|
-
if (
|
|
150
|
-
|
|
151
|
-
setTimeout(() =>
|
|
184
|
+
if (isMenuBarItem && hasChildren) {
|
|
185
|
+
openMenu();
|
|
186
|
+
setTimeout(async () => {
|
|
187
|
+
await tick();
|
|
188
|
+
menuRef?.focusLastMenuItem();
|
|
189
|
+
}, 10);
|
|
152
190
|
event.preventDefault();
|
|
191
|
+
event.stopPropagation();
|
|
153
192
|
return false;
|
|
154
|
-
}
|
|
155
|
-
|
|
193
|
+
}
|
|
194
|
+
if (!isMenuBarItem) {
|
|
195
|
+
focusPreviousMenuItem();
|
|
156
196
|
event.preventDefault();
|
|
197
|
+
event.stopPropagation();
|
|
157
198
|
return false;
|
|
158
199
|
}
|
|
200
|
+
break;
|
|
159
201
|
case "Escape":
|
|
160
202
|
open = false;
|
|
161
|
-
|
|
203
|
+
closeAllMenus();
|
|
162
204
|
event.preventDefault();
|
|
205
|
+
event.stopPropagation();
|
|
163
206
|
return false;
|
|
164
207
|
}
|
|
165
208
|
}
|
|
166
209
|
};
|
|
167
210
|
const onMouseEnter = (event) => {
|
|
168
|
-
|
|
169
|
-
menuItemRef?.focus();
|
|
170
|
-
}, 10);
|
|
211
|
+
menuItemRef?.focus();
|
|
171
212
|
};
|
|
172
|
-
const
|
|
213
|
+
const onClick = (event) => {
|
|
173
214
|
if (!disabled) {
|
|
174
215
|
if (hasChildren) {
|
|
175
|
-
|
|
216
|
+
if (!$openValues.includes(value)) {
|
|
217
|
+
openMenu();
|
|
218
|
+
if ($usingKeyboard) {
|
|
219
|
+
setTimeout(async () => {
|
|
220
|
+
await tick();
|
|
221
|
+
menuRef?.focusFirstMenuItem();
|
|
222
|
+
}, 10);
|
|
223
|
+
}
|
|
224
|
+
} else {
|
|
225
|
+
closeMenu();
|
|
226
|
+
}
|
|
176
227
|
event.preventDefault();
|
|
177
228
|
event.stopPropagation();
|
|
178
229
|
return false;
|
|
179
230
|
} else {
|
|
180
231
|
raiseSelect(value);
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
const onClickOutside = (event) => {
|
|
186
|
-
const {
|
|
187
|
-
detail: { mouseEvent }
|
|
188
|
-
} = event;
|
|
189
|
-
let element = mouseEvent.target;
|
|
190
|
-
while (element) {
|
|
191
|
-
if (element.getAttribute("data-root-value") === rootValue) {
|
|
192
|
-
return;
|
|
232
|
+
closeAllMenus();
|
|
233
|
+
event.preventDefault();
|
|
234
|
+
event.stopPropagation();
|
|
235
|
+
return false;
|
|
193
236
|
}
|
|
194
|
-
element = element.parentElement;
|
|
195
237
|
}
|
|
196
|
-
closeMenu?.(true);
|
|
197
238
|
};
|
|
198
239
|
setContext(MENU_ITEM_CONTEXT_KEY, {
|
|
240
|
+
isMenuBarItem: false,
|
|
241
|
+
openValues,
|
|
199
242
|
rootValue,
|
|
200
243
|
depth: depth + 1,
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
},
|
|
204
|
-
unregister: (menuItem) => {
|
|
205
|
-
children.set($children.filter((x) => x.value !== menuItem.value));
|
|
206
|
-
},
|
|
207
|
-
closeMenu: (recursive) => {
|
|
208
|
-
open = false;
|
|
209
|
-
if (recursive) {
|
|
210
|
-
closeMenu?.(recursive);
|
|
211
|
-
}
|
|
212
|
-
if (!recursive || depth === 0) {
|
|
213
|
-
menuItemRef?.focus();
|
|
214
|
-
}
|
|
244
|
+
closeContainingMenu: () => {
|
|
245
|
+
closeMenu();
|
|
215
246
|
},
|
|
216
|
-
focusPrevious: (currentValue) => focusPreviousChild($children, currentValue),
|
|
217
|
-
focusNext: (currentValue) => focusNextChild($children, currentValue),
|
|
218
247
|
onOpen: raiseOpen,
|
|
219
248
|
onClose: raiseClose,
|
|
220
249
|
onSelect: raiseSelect
|
|
@@ -230,14 +259,13 @@ setContext(MENU_ITEM_CONTEXT_KEY, {
|
|
|
230
259
|
bind:this={menuItemRef}
|
|
231
260
|
class="sterling-menu-item"
|
|
232
261
|
class:composed
|
|
233
|
-
class:disabled
|
|
234
262
|
class:using-keyboard={usingKeyboard}
|
|
235
263
|
data-value={value}
|
|
236
264
|
data-root-value={rootValue}
|
|
265
|
+
{disabled}
|
|
237
266
|
{role}
|
|
238
267
|
tabindex={0}
|
|
239
268
|
type="button"
|
|
240
|
-
use:clickOutside
|
|
241
269
|
on:blur
|
|
242
270
|
on:click
|
|
243
271
|
on:dblclick
|
|
@@ -269,26 +297,39 @@ setContext(MENU_ITEM_CONTEXT_KEY, {
|
|
|
269
297
|
on:pointerout
|
|
270
298
|
on:pointerup
|
|
271
299
|
on:wheel
|
|
272
|
-
on:click={
|
|
273
|
-
on:click_outside={onClickOutside}
|
|
300
|
+
on:click={onClick}
|
|
274
301
|
on:keydown={onKeyDown}
|
|
275
302
|
on:mouseenter={onMouseEnter}
|
|
276
303
|
{...$$restProps}
|
|
277
304
|
>
|
|
278
305
|
<div class="item" id={displayId}>
|
|
279
|
-
<slot
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
306
|
+
<slot
|
|
307
|
+
name="item"
|
|
308
|
+
{checked}
|
|
309
|
+
{depth}
|
|
310
|
+
{disabled}
|
|
311
|
+
{hasChildren}
|
|
312
|
+
{isMenuBarItem}
|
|
313
|
+
{open}
|
|
314
|
+
{role}
|
|
315
|
+
{text}
|
|
316
|
+
{value}
|
|
317
|
+
>
|
|
318
|
+
<MenuItemDisplay {checked} {disabled} {hasChildren} {isMenuBarItem} menuItemRole={role}
|
|
319
|
+
>{text}</MenuItemDisplay
|
|
285
320
|
>
|
|
286
321
|
</slot>
|
|
287
322
|
</div>
|
|
288
323
|
{#if menuItemRef && open && hasChildren}
|
|
289
|
-
<
|
|
290
|
-
|
|
291
|
-
|
|
324
|
+
<Popover
|
|
325
|
+
reference={menuItemRef}
|
|
326
|
+
placement={isMenuBarItem ? 'bottom-start' : 'right-start'}
|
|
327
|
+
{open}
|
|
328
|
+
>
|
|
329
|
+
<Menu bind:this={menuRef} id={menuId}>
|
|
330
|
+
<slot {depth} {disabled} />
|
|
331
|
+
</Menu>
|
|
332
|
+
</Popover>
|
|
292
333
|
{/if}
|
|
293
334
|
</button>
|
|
294
335
|
|
package/MenuItem.svelte.d.ts
CHANGED
|
@@ -5,12 +5,11 @@ declare const __propDef: {
|
|
|
5
5
|
checked?: boolean | undefined;
|
|
6
6
|
composed?: boolean | undefined;
|
|
7
7
|
disabled?: boolean | undefined;
|
|
8
|
-
open?: boolean | undefined;
|
|
9
8
|
value: string;
|
|
10
9
|
role?: "menuitem" | "menuitemcheckbox" | "menuitemradio" | undefined;
|
|
11
10
|
text?: string | undefined;
|
|
12
|
-
click?: (() => void) | undefined;
|
|
13
11
|
blur?: (() => void) | undefined;
|
|
12
|
+
click?: (() => void) | undefined;
|
|
14
13
|
focus?: ((options?: FocusOptions) => void) | undefined;
|
|
15
14
|
};
|
|
16
15
|
events: {
|
|
@@ -45,6 +44,9 @@ declare const __propDef: {
|
|
|
45
44
|
pointerout: PointerEvent;
|
|
46
45
|
pointerup: PointerEvent;
|
|
47
46
|
wheel: WheelEvent;
|
|
47
|
+
close: CustomEvent<any>;
|
|
48
|
+
open: CustomEvent<any>;
|
|
49
|
+
select: CustomEvent<any>;
|
|
48
50
|
} & {
|
|
49
51
|
[evt: string]: CustomEvent<any>;
|
|
50
52
|
};
|
|
@@ -54,6 +56,7 @@ declare const __propDef: {
|
|
|
54
56
|
depth: number;
|
|
55
57
|
disabled: boolean;
|
|
56
58
|
hasChildren: boolean;
|
|
59
|
+
isMenuBarItem: boolean | undefined;
|
|
57
60
|
open: boolean;
|
|
58
61
|
role: "menuitem" | "menuitemcheckbox" | "menuitemradio";
|
|
59
62
|
text: string | undefined;
|
|
@@ -69,8 +72,8 @@ export type MenuItemProps = typeof __propDef.props;
|
|
|
69
72
|
export type MenuItemEvents = typeof __propDef.events;
|
|
70
73
|
export type MenuItemSlots = typeof __propDef.slots;
|
|
71
74
|
export default class MenuItem extends SvelteComponentTyped<MenuItemProps, MenuItemEvents, MenuItemSlots> {
|
|
72
|
-
get click(): () => void;
|
|
73
75
|
get blur(): () => void;
|
|
76
|
+
get click(): () => void;
|
|
74
77
|
get focus(): (options?: FocusOptions | undefined) => void;
|
|
75
78
|
}
|
|
76
79
|
export {};
|
package/MenuItem.types.d.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import type { Writable } from 'svelte/store';
|
|
2
|
+
import type { MENU_ITEM_ROLES } from './MenuItem.constants';
|
|
3
|
+
type MenuItemRoleTuple = typeof MENU_ITEM_ROLES;
|
|
4
|
+
export type MenuItemRole = MenuItemRoleTuple[number];
|
|
1
5
|
export type MenuItemRegistration = {
|
|
2
6
|
value: string;
|
|
3
7
|
open: () => void;
|
|
@@ -5,14 +9,19 @@ export type MenuItemRegistration = {
|
|
|
5
9
|
focus: () => void;
|
|
6
10
|
};
|
|
7
11
|
export type MenuItemContext = {
|
|
12
|
+
/**
|
|
13
|
+
* If the menu item is a top-elvel item in a menu bar
|
|
14
|
+
*/
|
|
15
|
+
isMenuBarItem?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* The menu item values for the chain of open menus.
|
|
18
|
+
*/
|
|
19
|
+
openValues: Writable<string[]>;
|
|
8
20
|
rootValue?: string;
|
|
9
21
|
depth?: number;
|
|
10
|
-
|
|
11
|
-
unregister?: (menuItem: MenuItemRegistration) => void;
|
|
12
|
-
focusPrevious?: (currentValue: string) => void;
|
|
13
|
-
focusNext?: (currentValue: string) => void;
|
|
14
|
-
closeMenu?: (recursive?: boolean) => void;
|
|
22
|
+
closeContainingMenu?: () => void;
|
|
15
23
|
onOpen?: (value: string) => void;
|
|
16
24
|
onClose?: (value: string) => void;
|
|
17
25
|
onSelect?: (value: string) => void;
|
|
18
26
|
};
|
|
27
|
+
export {};
|
package/MenuItem.utils.d.ts
CHANGED
|
@@ -3,3 +3,5 @@ export declare const focusPreviousChild: (children: MenuItemRegistration[], curr
|
|
|
3
3
|
export declare const focusNextChild: (children: MenuItemRegistration[], currentValue: string) => void;
|
|
4
4
|
export declare const focusFirstChild: (children: MenuItemRegistration[]) => void;
|
|
5
5
|
export declare const focusLastChild: (children: MenuItemRegistration[]) => void;
|
|
6
|
+
export declare const isElementMenuItem: (candidate: Element | null | undefined) => boolean;
|
|
7
|
+
export declare const isElementEnabledMenuItem: (candidate: Element | null | undefined) => boolean;
|
package/MenuItem.utils.js
CHANGED
|
@@ -18,3 +18,19 @@ export const focusFirstChild = (children) => {
|
|
|
18
18
|
export const focusLastChild = (children) => {
|
|
19
19
|
children?.[children.length - 1]?.focus();
|
|
20
20
|
};
|
|
21
|
+
export const isElementMenuItem = (candidate) => {
|
|
22
|
+
if (!candidate)
|
|
23
|
+
return false;
|
|
24
|
+
const dataValue = candidate?.getAttribute('data-value');
|
|
25
|
+
const role = candidate?.getAttribute('role');
|
|
26
|
+
return ((role === 'menuitem' || role === 'menuitemcheckbox' || role === 'menuitemradio') &&
|
|
27
|
+
dataValue !== null &&
|
|
28
|
+
dataValue !== undefined);
|
|
29
|
+
};
|
|
30
|
+
export const isElementEnabledMenuItem = (candidate) => {
|
|
31
|
+
if (!isElementMenuItem(candidate)) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
const disabled = candidate?.getAttribute('disabled');
|
|
35
|
+
return disabled === null || disabled === 'false';
|
|
36
|
+
};
|
package/MenuItemDisplay.svelte
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script>export let checked = false;
|
|
2
2
|
export let disabled = false;
|
|
3
3
|
export let hasChildren = false;
|
|
4
|
+
export let isMenuBarItem = false;
|
|
4
5
|
export let menuItemRole = "menuitem";
|
|
5
6
|
</script>
|
|
6
7
|
|
|
@@ -19,7 +20,7 @@ export let menuItemRole = "menuitem";
|
|
|
19
20
|
<slot name="shortcut" />
|
|
20
21
|
</div>
|
|
21
22
|
{:else}
|
|
22
|
-
<div class="chevron" class:has-children={hasChildren} />
|
|
23
|
+
<div class="chevron" class:has-children={!isMenuBarItem && hasChildren} />
|
|
23
24
|
{/if}
|
|
24
25
|
</div>
|
|
25
26
|
|