@functionalcms/svelte-components 2.33.2 → 2.34.2
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/dist/components/Link.svelte +290 -290
- package/dist/components/agnostic/Alert/Alert.svelte +310 -0
- package/dist/components/agnostic/Alert/Alert.svelte.d.ts +31 -0
- package/dist/components/agnostic/Avatar/Avatar.svelte +123 -0
- package/dist/components/agnostic/Avatar/Avatar.svelte.d.ts +26 -0
- package/dist/components/agnostic/Avatar/AvatarGroup.svelte +106 -0
- package/dist/components/agnostic/Avatar/AvatarGroup.svelte.d.ts +29 -0
- package/dist/components/agnostic/Breadcrumb/Breadcrumb.svelte +56 -0
- package/dist/components/agnostic/Breadcrumb/Breadcrumb.svelte.d.ts +20 -0
- package/dist/components/agnostic/Breadcrumb/api.d.ts +4 -0
- package/dist/components/agnostic/Breadcrumb/api.js +1 -0
- package/dist/components/agnostic/Button/Button.svelte +345 -0
- package/dist/components/agnostic/Button/Button.svelte.d.ts +43 -0
- package/dist/components/agnostic/Button/ButtonGroup.svelte +20 -0
- package/dist/components/agnostic/Button/ButtonGroup.svelte.d.ts +23 -0
- package/dist/components/agnostic/Button/button-base.css +12 -0
- package/dist/components/agnostic/Button/button-core.css +237 -0
- package/dist/components/agnostic/Button/button-empty.css +31 -0
- package/dist/components/agnostic/Button/button-group.css +8 -0
- package/dist/components/agnostic/Card/Card.svelte +133 -0
- package/dist/components/agnostic/Card/Card.svelte.d.ts +31 -0
- package/dist/components/agnostic/ChoiceInput/ChoiceInput.svelte +337 -0
- package/dist/components/agnostic/ChoiceInput/ChoiceInput.svelte.d.ts +37 -0
- package/dist/components/agnostic/ChoiceInput/api.d.ts +7 -0
- package/dist/components/agnostic/ChoiceInput/api.js +1 -0
- package/dist/components/agnostic/Close/Close.svelte +120 -0
- package/dist/components/agnostic/Close/Close.svelte.d.ts +23 -0
- package/dist/components/agnostic/Close/api.d.ts +1 -0
- package/dist/components/agnostic/Close/api.js +1 -0
- package/dist/components/agnostic/Dialog/Dialog.svelte +265 -0
- package/dist/components/agnostic/Dialog/Dialog.svelte.d.ts +39 -0
- package/dist/components/agnostic/Dialog/SvelteA11yDialog.svelte +103 -0
- package/dist/components/agnostic/Dialog/SvelteA11yDialog.svelte.d.ts +30 -0
- package/dist/components/agnostic/Dialog/a11y-dialog.d.ts +56 -0
- package/dist/components/agnostic/Dialog/a11y-dialog.js +216 -0
- package/dist/components/agnostic/Dialog/dom-utils.d.ts +26 -0
- package/dist/components/agnostic/Dialog/dom-utils.js +206 -0
- package/dist/components/agnostic/Disclose/Disclose.svelte +102 -0
- package/dist/components/agnostic/Disclose/Disclose.svelte.d.ts +23 -0
- package/dist/components/agnostic/Divider/Divider.svelte +139 -0
- package/dist/components/agnostic/Divider/Divider.svelte.d.ts +25 -0
- package/dist/components/agnostic/Divider/api.d.ts +3 -0
- package/dist/components/agnostic/Divider/api.js +1 -0
- package/dist/components/agnostic/Drawer/Drawer.svelte +30 -0
- package/dist/components/agnostic/Drawer/Drawer.svelte.d.ts +28 -0
- package/dist/components/agnostic/Drawer/api.d.ts +1 -0
- package/dist/components/agnostic/Drawer/api.js +1 -0
- package/dist/components/agnostic/EmptyState/EmptyState.svelte +46 -0
- package/dist/components/agnostic/EmptyState/EmptyState.svelte.d.ts +23 -0
- package/dist/components/agnostic/Header/Header.svelte +104 -0
- package/dist/components/agnostic/Header/Header.svelte.d.ts +26 -0
- package/dist/components/agnostic/Header/HeaderNav.svelte +28 -0
- package/dist/components/agnostic/Header/HeaderNav.svelte.d.ts +20 -0
- package/dist/components/agnostic/Header/HeaderNavItem.svelte +30 -0
- package/dist/components/agnostic/Header/HeaderNavItem.svelte.d.ts +20 -0
- package/dist/components/agnostic/Icon/Icon.svelte +180 -0
- package/dist/components/agnostic/Icon/Icon.svelte.d.ts +23 -0
- package/dist/components/agnostic/Icon/api.d.ts +2 -0
- package/dist/components/agnostic/Icon/api.js +1 -0
- package/dist/components/agnostic/Input/Input.svelte +415 -0
- package/dist/components/agnostic/Input/Input.svelte.d.ts +45 -0
- package/dist/components/agnostic/Input/InputAddonItem.svelte +42 -0
- package/dist/components/agnostic/Input/InputAddonItem.svelte.d.ts +33 -0
- package/dist/components/agnostic/Loader/Loader.svelte +113 -0
- package/dist/components/agnostic/Loader/Loader.svelte.d.ts +20 -0
- package/dist/components/agnostic/Menu/Menu.svelte +466 -0
- package/dist/components/agnostic/Menu/Menu.svelte.d.ts +31 -0
- package/dist/components/agnostic/Menu/MenuItem.svelte +117 -0
- package/dist/components/agnostic/Menu/MenuItem.svelte.d.ts +29 -0
- package/dist/components/agnostic/Progress/Progress.svelte +50 -0
- package/dist/components/agnostic/Progress/Progress.svelte.d.ts +20 -0
- package/dist/components/agnostic/Select/Select.svelte +141 -0
- package/dist/components/agnostic/Select/Select.svelte.d.ts +32 -0
- package/dist/components/agnostic/Spinner/Spinner.svelte +105 -0
- package/dist/components/agnostic/Spinner/Spinner.svelte.d.ts +19 -0
- package/dist/components/agnostic/Switch/Switch.svelte +275 -0
- package/dist/components/agnostic/Switch/Switch.svelte.d.ts +45 -0
- package/dist/components/agnostic/Table/Table.svelte +508 -0
- package/dist/components/agnostic/Table/Table.svelte.d.ts +36 -0
- package/dist/components/agnostic/Table/TableCustomRenderComponent.svelte +13 -0
- package/dist/components/agnostic/Table/TableCustomRenderComponent.svelte.d.ts +25 -0
- package/dist/components/agnostic/Tabs/TabButtonCustom.svelte +65 -0
- package/dist/components/agnostic/Tabs/TabButtonCustom.svelte.d.ts +35 -0
- package/dist/components/agnostic/Tabs/Tabs.svelte +330 -0
- package/dist/components/agnostic/Tabs/Tabs.svelte.d.ts +34 -0
- package/dist/components/agnostic/Tabs/api.d.ts +10 -0
- package/dist/components/agnostic/Tabs/api.js +1 -0
- package/dist/components/agnostic/Tag/Tag.svelte +74 -0
- package/dist/components/agnostic/Tag/Tag.svelte.d.ts +23 -0
- package/dist/components/agnostic/Tag/TagSlots.svelte +51 -0
- package/dist/components/agnostic/Tag/TagSlots.svelte.d.ts +16 -0
- package/dist/components/agnostic/Toasts/Toasts.svelte +46 -0
- package/dist/components/agnostic/Toasts/Toasts.svelte.d.ts +22 -0
- package/dist/components/agnostic/Tooltip/Tooltip.svelte +103 -0
- package/dist/components/agnostic/Tooltip/Tooltip.svelte.d.ts +23 -0
- package/dist/components/agnostic/Tooltip/TooltipSlots.svelte +81 -0
- package/dist/components/agnostic/Tooltip/TooltipSlots.svelte.d.ts +16 -0
- package/dist/components/agnostic/Tooltip/api.d.ts +1 -0
- package/dist/components/agnostic/Tooltip/api.js +1 -0
- package/dist/components/agnostic/animation.css +37 -0
- package/dist/index.d.ts +30 -1
- package/dist/index.js +30 -1
- package/package.json +1 -4
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** @typedef {typeof __propDef.props} InputAddonItemProps */
|
|
2
|
+
/** @typedef {typeof __propDef.events} InputAddonItemEvents */
|
|
3
|
+
/** @typedef {typeof __propDef.slots} InputAddonItemSlots */
|
|
4
|
+
export default class InputAddonItem extends SvelteComponent<{
|
|
5
|
+
css?: string | undefined;
|
|
6
|
+
addonLeft?: boolean | undefined;
|
|
7
|
+
addonRight?: boolean | undefined;
|
|
8
|
+
}, {
|
|
9
|
+
[evt: string]: CustomEvent<any>;
|
|
10
|
+
}, {
|
|
11
|
+
default: {};
|
|
12
|
+
}> {
|
|
13
|
+
}
|
|
14
|
+
export type InputAddonItemProps = typeof __propDef.props;
|
|
15
|
+
export type InputAddonItemEvents = typeof __propDef.events;
|
|
16
|
+
export type InputAddonItemSlots = typeof __propDef.slots;
|
|
17
|
+
import { SvelteComponent } from "svelte";
|
|
18
|
+
declare const __propDef: {
|
|
19
|
+
props: {
|
|
20
|
+
css?: string | undefined;
|
|
21
|
+
addonLeft?: boolean | undefined;
|
|
22
|
+
addonRight?: boolean | undefined;
|
|
23
|
+
};
|
|
24
|
+
events: {
|
|
25
|
+
[evt: string]: CustomEvent<any>;
|
|
26
|
+
};
|
|
27
|
+
slots: {
|
|
28
|
+
default: {};
|
|
29
|
+
};
|
|
30
|
+
exports?: undefined;
|
|
31
|
+
bindings?: undefined;
|
|
32
|
+
};
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
<style>
|
|
2
|
+
.loader {
|
|
3
|
+
--loading-color: var(--functional-loading-color, var(--functional-dark));
|
|
4
|
+
--loading-size: var(--fluid-16);
|
|
5
|
+
--loading-size-small: var(--fluid-12);
|
|
6
|
+
--loading-size-large: var(--fluid-18);
|
|
7
|
+
|
|
8
|
+
position: relative;
|
|
9
|
+
box-sizing: border-box;
|
|
10
|
+
animation: blink 1s infinite;
|
|
11
|
+
animation-delay: 250ms;
|
|
12
|
+
|
|
13
|
+
/* Make up for negative positioning */
|
|
14
|
+
margin-left: var(--loading-size);
|
|
15
|
+
|
|
16
|
+
/* Initially set zero until aria-busy becomes true */
|
|
17
|
+
opacity: 0%;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.loader,
|
|
21
|
+
.loader::before,
|
|
22
|
+
.loader::after {
|
|
23
|
+
width: calc(var(--loading-size) / 2);
|
|
24
|
+
height: calc(var(--loading-size) / 2);
|
|
25
|
+
border-radius: var(--fluid-6);
|
|
26
|
+
background-color: var(--loading-color);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/* SMALL */
|
|
30
|
+
.loader-small,
|
|
31
|
+
.loader-small::before,
|
|
32
|
+
.loader-small::after {
|
|
33
|
+
width: calc(var(--loading-size-small) / 2);
|
|
34
|
+
height: calc(var(--loading-size-small) / 2);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* LARGE */
|
|
38
|
+
.loader-large,
|
|
39
|
+
.loader-large::before,
|
|
40
|
+
.loader-large::after {
|
|
41
|
+
width: calc(var(--loading-size-large) / 2);
|
|
42
|
+
height: calc(var(--loading-size-large) / 2);
|
|
43
|
+
border-radius: var(--fluid-8);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.loader::before,
|
|
47
|
+
.loader::after {
|
|
48
|
+
content: "";
|
|
49
|
+
display: inline-block;
|
|
50
|
+
position: absolute;
|
|
51
|
+
top: 0;
|
|
52
|
+
animation: blink 1s infinite;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.loader::before {
|
|
56
|
+
left: calc(-1 * var(--loading-size));
|
|
57
|
+
animation-delay: 0s;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.loader::after {
|
|
61
|
+
left: var(--loading-size);
|
|
62
|
+
animation-delay: 500ms;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/* SMALL */
|
|
66
|
+
.loader-small::before {
|
|
67
|
+
left: calc(-1 * var(--loading-size-small));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.loader-small::after {
|
|
71
|
+
left: var(--loading-size-small);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/* LARGE */
|
|
75
|
+
.loader-large::before {
|
|
76
|
+
left: calc(-1 * var(--loading-size-large));
|
|
77
|
+
animation-delay: 0s;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.loader-large::after {
|
|
81
|
+
left: var(--loading-size-large);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Setting aria-busy to true results in corresponding opacity change to visually show spinner.
|
|
86
|
+
*/
|
|
87
|
+
.loader[aria-busy="true"] {
|
|
88
|
+
opacity: 100%;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@keyframes blink {
|
|
92
|
+
50% {
|
|
93
|
+
background-color: transparent;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
@media (prefers-reduced-motion), (update: slow) {
|
|
98
|
+
.loader,
|
|
99
|
+
.loader::before,
|
|
100
|
+
.loader::after {
|
|
101
|
+
transition-duration: 0.001ms !important;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
</style>
|
|
106
|
+
<script>export let ariaLabel = "Loading\u2026";
|
|
107
|
+
export let size = "";
|
|
108
|
+
export let loaderClasses = ["loader", size ? `loader-${size}` : ""].filter((c) => c).join(" ");
|
|
109
|
+
</script>
|
|
110
|
+
|
|
111
|
+
<div class={loaderClasses} role="status" aria-live="polite" aria-busy="true">
|
|
112
|
+
<span class="screenreader-only">{ariaLabel}</span>
|
|
113
|
+
</div>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { SvelteComponent } from "svelte";
|
|
2
|
+
declare const __propDef: {
|
|
3
|
+
props: {
|
|
4
|
+
ariaLabel?: string;
|
|
5
|
+
size?: "small" | "large" | "";
|
|
6
|
+
loaderClasses?: string;
|
|
7
|
+
};
|
|
8
|
+
events: {
|
|
9
|
+
[evt: string]: CustomEvent<any>;
|
|
10
|
+
};
|
|
11
|
+
slots: {};
|
|
12
|
+
exports?: {} | undefined;
|
|
13
|
+
bindings?: string | undefined;
|
|
14
|
+
};
|
|
15
|
+
export type LoaderProps = typeof __propDef.props;
|
|
16
|
+
export type LoaderEvents = typeof __propDef.events;
|
|
17
|
+
export type LoaderSlots = typeof __propDef.slots;
|
|
18
|
+
export default class Loader extends SvelteComponent<LoaderProps, LoaderEvents, LoaderSlots> {
|
|
19
|
+
}
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
<script>import { onDestroy, onMount } from "svelte";
|
|
2
|
+
export let id;
|
|
3
|
+
export let type = "simple";
|
|
4
|
+
export let size = "";
|
|
5
|
+
export let menuTitle;
|
|
6
|
+
export let menuItems = [];
|
|
7
|
+
export let isDisabled = false;
|
|
8
|
+
export let isRounded = false;
|
|
9
|
+
export let isBordered = false;
|
|
10
|
+
export let isItemsRight = false;
|
|
11
|
+
export let icon = "\u25BE";
|
|
12
|
+
export let onOpen;
|
|
13
|
+
export let onClose;
|
|
14
|
+
export let closeOnClickOutside = true;
|
|
15
|
+
export let closeOnSelect = true;
|
|
16
|
+
let rootRef;
|
|
17
|
+
let triggerRef;
|
|
18
|
+
let menuItemRefs = [];
|
|
19
|
+
$: menuItemRefs = [];
|
|
20
|
+
let expanded = false;
|
|
21
|
+
const setExpanded = (b) => expanded = b;
|
|
22
|
+
let selectedItem = -1;
|
|
23
|
+
const setSelectedItem = (n) => selectedItem = n;
|
|
24
|
+
const setOpened = (open) => {
|
|
25
|
+
if (open && onOpen) {
|
|
26
|
+
onOpen(selectedItem);
|
|
27
|
+
} else if (onClose) {
|
|
28
|
+
onClose();
|
|
29
|
+
}
|
|
30
|
+
setExpanded(open);
|
|
31
|
+
};
|
|
32
|
+
const focusItem = (index, direction) => {
|
|
33
|
+
let i = index;
|
|
34
|
+
if (direction === "asc") {
|
|
35
|
+
i += 1;
|
|
36
|
+
} else if (direction === "desc") {
|
|
37
|
+
i -= 1;
|
|
38
|
+
}
|
|
39
|
+
if (i < 0) {
|
|
40
|
+
i = menuItems.length - 1;
|
|
41
|
+
} else if (i >= menuItems.length) {
|
|
42
|
+
i = 0;
|
|
43
|
+
}
|
|
44
|
+
const nextMenuItem = menuItemRefs[i];
|
|
45
|
+
if (nextMenuItem) {
|
|
46
|
+
if (nextMenuItem.isDisabled() && direction) {
|
|
47
|
+
focusItem(i, direction);
|
|
48
|
+
} else {
|
|
49
|
+
nextMenuItem.focus();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
const focusTriggerButton = () => triggerRef && triggerRef.focus();
|
|
54
|
+
const isInside = (el) => {
|
|
55
|
+
if (rootRef) {
|
|
56
|
+
const children = rootRef.querySelectorAll("*");
|
|
57
|
+
for (let i = 0; i < children.length; i += 1) {
|
|
58
|
+
const child = children[i];
|
|
59
|
+
if (el === child) {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return false;
|
|
65
|
+
};
|
|
66
|
+
const clickedOutside = (ev) => {
|
|
67
|
+
if (expanded && closeOnClickOutside) {
|
|
68
|
+
if (!isInside(ev.target)) {
|
|
69
|
+
setExpanded(false);
|
|
70
|
+
focusTriggerButton();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
onMount(() => {
|
|
75
|
+
if (typeof window !== "undefined") {
|
|
76
|
+
document.addEventListener("click", clickedOutside);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
onDestroy(() => {
|
|
80
|
+
if (typeof window !== "undefined") {
|
|
81
|
+
document.removeEventListener("click", clickedOutside);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
let triggerSizeClasses;
|
|
85
|
+
let itemSizeClasses;
|
|
86
|
+
switch (size) {
|
|
87
|
+
case "small":
|
|
88
|
+
triggerSizeClasses = "menu-trigger-small";
|
|
89
|
+
itemSizeClasses = "menu-item-small";
|
|
90
|
+
break;
|
|
91
|
+
case "large":
|
|
92
|
+
triggerSizeClasses = "menu-trigger-large";
|
|
93
|
+
itemSizeClasses = "menu-item-large";
|
|
94
|
+
break;
|
|
95
|
+
default:
|
|
96
|
+
triggerSizeClasses = "";
|
|
97
|
+
itemSizeClasses = "";
|
|
98
|
+
}
|
|
99
|
+
const dotBarClasses = [
|
|
100
|
+
type === "hamburger" ? "bar" : "dot"
|
|
101
|
+
].filter((cls) => cls).join(" ");
|
|
102
|
+
const triggerClasses = [
|
|
103
|
+
type === "simple" ? "menu-trigger" : "",
|
|
104
|
+
triggerSizeClasses,
|
|
105
|
+
isBordered ? "menu-trigger-bordered" : "",
|
|
106
|
+
isRounded ? "menu-trigger-rounded" : "",
|
|
107
|
+
type !== "simple" ? "btn-base" : "",
|
|
108
|
+
type !== "simple" ? "btn-blank" : "",
|
|
109
|
+
type === "kebab" ? "btn-kebab" : "",
|
|
110
|
+
type === "meatball" ? "btn-meatball" : "",
|
|
111
|
+
type === "hamburger" ? "btn-hamburger" : ""
|
|
112
|
+
].filter((cls) => cls).join(" ");
|
|
113
|
+
const itemClasses = [itemSizeClasses, isRounded ? "menu-item-rounded" : ""].filter((cls) => cls).join(" ");
|
|
114
|
+
const afterOpened = () => {
|
|
115
|
+
requestAnimationFrame(() => {
|
|
116
|
+
if (selectedItem < 1) {
|
|
117
|
+
setSelectedItem(0);
|
|
118
|
+
onMenuItemKeyDown("Home", 0);
|
|
119
|
+
} else {
|
|
120
|
+
focusItem(selectedItem);
|
|
121
|
+
setSelectedItem(selectedItem);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
};
|
|
125
|
+
const onMenuItemKeyDown = (evOrString, index) => {
|
|
126
|
+
const key = typeof evOrString === "string" ? evOrString : evOrString.key;
|
|
127
|
+
switch (key) {
|
|
128
|
+
case "Up":
|
|
129
|
+
case "ArrowUp":
|
|
130
|
+
focusItem(index, "desc");
|
|
131
|
+
break;
|
|
132
|
+
case "Down":
|
|
133
|
+
case "ArrowDown":
|
|
134
|
+
focusItem(index, "asc");
|
|
135
|
+
break;
|
|
136
|
+
case "Home":
|
|
137
|
+
case "ArrowHome":
|
|
138
|
+
focusItem(0);
|
|
139
|
+
break;
|
|
140
|
+
case "End":
|
|
141
|
+
case "ArrowEnd":
|
|
142
|
+
focusItem(menuItems.length - 1);
|
|
143
|
+
break;
|
|
144
|
+
case "Enter":
|
|
145
|
+
case "Space":
|
|
146
|
+
focusItem(index);
|
|
147
|
+
setSelectedItem(index);
|
|
148
|
+
if (closeOnSelect) {
|
|
149
|
+
setOpened(false);
|
|
150
|
+
focusTriggerButton();
|
|
151
|
+
}
|
|
152
|
+
break;
|
|
153
|
+
case "Escape":
|
|
154
|
+
setOpened(false);
|
|
155
|
+
focusTriggerButton();
|
|
156
|
+
break;
|
|
157
|
+
case "Tab":
|
|
158
|
+
if (typeof evOrString !== "string") {
|
|
159
|
+
evOrString.preventDefault();
|
|
160
|
+
}
|
|
161
|
+
break;
|
|
162
|
+
default:
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
if (typeof evOrString !== "string") {
|
|
166
|
+
evOrString.preventDefault();
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
const onTriggerButtonKeyDown = (e) => {
|
|
170
|
+
switch (e.key) {
|
|
171
|
+
case "Down":
|
|
172
|
+
case "ArrowDown":
|
|
173
|
+
if (!expanded) {
|
|
174
|
+
setOpened(true);
|
|
175
|
+
afterOpened();
|
|
176
|
+
e.preventDefault();
|
|
177
|
+
}
|
|
178
|
+
break;
|
|
179
|
+
case "Escape":
|
|
180
|
+
if (expanded) {
|
|
181
|
+
setOpened(false);
|
|
182
|
+
focusTriggerButton();
|
|
183
|
+
}
|
|
184
|
+
break;
|
|
185
|
+
default:
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
const onTriggerButtonClicked = () => {
|
|
189
|
+
const toggled = !expanded;
|
|
190
|
+
setOpened(toggled);
|
|
191
|
+
setTimeout(() => {
|
|
192
|
+
if (toggled) {
|
|
193
|
+
afterOpened();
|
|
194
|
+
} else if (closeOnSelect) {
|
|
195
|
+
setOpened(false);
|
|
196
|
+
focusTriggerButton();
|
|
197
|
+
}
|
|
198
|
+
}, 10);
|
|
199
|
+
};
|
|
200
|
+
$: menuItemClasses = (isSelected) => {
|
|
201
|
+
return [
|
|
202
|
+
`menu-item`,
|
|
203
|
+
itemClasses,
|
|
204
|
+
isSelected ? "menu-item-selected" : ""
|
|
205
|
+
].filter((klass) => klass.length).join(" ");
|
|
206
|
+
};
|
|
207
|
+
$: menuItemsClasses = () => {
|
|
208
|
+
return [
|
|
209
|
+
isItemsRight ? "menu-items-right" : "",
|
|
210
|
+
!isItemsRight ? "menu-items" : ""
|
|
211
|
+
].filter((c) => c && c.length).join(" ");
|
|
212
|
+
};
|
|
213
|
+
$: onMenuItemClicked = (index) => {
|
|
214
|
+
setSelectedItem(index);
|
|
215
|
+
if (closeOnSelect) {
|
|
216
|
+
setOpened(false);
|
|
217
|
+
focusTriggerButton();
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
</script>
|
|
221
|
+
<div bind:this={rootRef} class="menu">
|
|
222
|
+
<button
|
|
223
|
+
bind:this={triggerRef}
|
|
224
|
+
class={triggerClasses}
|
|
225
|
+
aria-haspopup="true"
|
|
226
|
+
aria-expanded={expanded}
|
|
227
|
+
disabled={isDisabled}
|
|
228
|
+
on:keydown={onTriggerButtonKeyDown}
|
|
229
|
+
on:click={onTriggerButtonClicked}
|
|
230
|
+
>
|
|
231
|
+
{#if type === 'simple'}
|
|
232
|
+
{menuTitle}
|
|
233
|
+
<span class="menu-icon" aria-hidden="true">
|
|
234
|
+
{icon}
|
|
235
|
+
</span>
|
|
236
|
+
{:else}
|
|
237
|
+
<span class="screenreader-only">{menuTitle}</span>
|
|
238
|
+
<span class={dotBarClasses} />
|
|
239
|
+
<span class={dotBarClasses} />
|
|
240
|
+
<span class={dotBarClasses} />
|
|
241
|
+
{/if}
|
|
242
|
+
</button>
|
|
243
|
+
<div class={menuItemsClasses()} id={id} role="menu" hidden={!expanded}>
|
|
244
|
+
{#each menuItems as item, i}
|
|
245
|
+
<svelte:component
|
|
246
|
+
this={item.menuItemComponent}
|
|
247
|
+
bind:this={menuItemRefs[i]}
|
|
248
|
+
classes={menuItemClasses(selectedItem === i)}
|
|
249
|
+
isSelected={selectedItem === i}
|
|
250
|
+
disabled={item.isDisabled}
|
|
251
|
+
on:click={() => onMenuItemClicked(i)}
|
|
252
|
+
on:keydown={(ev) => onMenuItemKeyDown(ev, i)}
|
|
253
|
+
>
|
|
254
|
+
{item.label}
|
|
255
|
+
</svelte:component>
|
|
256
|
+
{/each}
|
|
257
|
+
</div>
|
|
258
|
+
</div>
|
|
259
|
+
<style>
|
|
260
|
+
.menu {
|
|
261
|
+
display: inline-flex;
|
|
262
|
+
position: relative;
|
|
263
|
+
background-color: inherit;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
:is(.menu-items, .menu-items-right) {
|
|
267
|
+
position: absolute;
|
|
268
|
+
background-color: var(--functional-light);
|
|
269
|
+
margin-block-start: var(--fluid-6);
|
|
270
|
+
z-index: 10;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
.menu-items {
|
|
274
|
+
right: initial;
|
|
275
|
+
left: 0;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
.menu-items-right {
|
|
279
|
+
left: initial;
|
|
280
|
+
right: 0;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.btn-base {
|
|
284
|
+
display: inline-flex;
|
|
285
|
+
align-items: center;
|
|
286
|
+
justify-content: center;
|
|
287
|
+
white-space: nowrap;
|
|
288
|
+
user-select: none;
|
|
289
|
+
appearance: none;
|
|
290
|
+
cursor: pointer;
|
|
291
|
+
box-sizing: border-box;
|
|
292
|
+
transition-property: all;
|
|
293
|
+
transition-duration: var(--functional-timing-medium);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Invisible buttons. Generally used for a Cancel or icon button that behaves like a button,
|
|
298
|
+
* semantically and for a11y, but, does so without all the typical "button chrome" behind it.
|
|
299
|
+
*/
|
|
300
|
+
:is(.btn-link, .btn-blank) {
|
|
301
|
+
font-family: var(--functional-btn-font-family, var(--functional-font-family-body));
|
|
302
|
+
font-size: var(--functional-btn-font-size, 1rem);
|
|
303
|
+
background-color: transparent;
|
|
304
|
+
border: 0;
|
|
305
|
+
border-radius: 0;
|
|
306
|
+
box-shadow: none;
|
|
307
|
+
transition: none;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/* Since blank buttons can be used for things like input addons we don't want to go crazy
|
|
311
|
+
on the side padding. As such, these have a good bit less then regular buttons. */
|
|
312
|
+
.btn-blank {
|
|
313
|
+
--functional-btn-blank-side-padding: var(--btn-blank-side-padding, 0.25rem);
|
|
314
|
+
|
|
315
|
+
padding-inline-start: var(--functional-btn-blank-side-padding);
|
|
316
|
+
padding-inline-end: var(--functional-btn-blank-side-padding);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/* A button blank with link color text */
|
|
320
|
+
.btn-link {
|
|
321
|
+
color: var(--functional-btn-primary, var(--functional-primary));
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
.btn-link:hover {
|
|
325
|
+
cursor: pointer;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
.menu-trigger {
|
|
329
|
+
display: flex;
|
|
330
|
+
align-items: center;
|
|
331
|
+
justify-content: space-between;
|
|
332
|
+
max-width: 100%;
|
|
333
|
+
background-color: var(--functional-btn-bgcolor, var(--functional-gray-light));
|
|
334
|
+
cursor: pointer;
|
|
335
|
+
text-align: left;
|
|
336
|
+
|
|
337
|
+
/* TODO -- can we compose some of this from the button styles? */
|
|
338
|
+
border-color: var(--functional-btn-bgcolor, var(--functional-gray-light));
|
|
339
|
+
border-style: solid;
|
|
340
|
+
border-width: var(--functional-btn-border-size, 1px);
|
|
341
|
+
font-size: inherit;
|
|
342
|
+
|
|
343
|
+
/* this can be overriden, but it might mess with the balance of the button heights across variants */
|
|
344
|
+
line-height: var(--functional-line-height, var(--fluid-20, 1.25rem));
|
|
345
|
+
padding-block-start: var(--functional-vertical-pad, 0.5rem);
|
|
346
|
+
padding-block-end: var(--functional-vertical-pad, 0.5rem);
|
|
347
|
+
padding-inline-start: var(--functional-side-padding, 0.75rem);
|
|
348
|
+
padding-inline-end: var(--functional-side-padding, 0.75rem);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
.menu-trigger[disabled] {
|
|
352
|
+
background: var(--functional-input-disabled-bg, var(--functional-disabled-bg)) !important;
|
|
353
|
+
color: var(--functional-input-disabled-color, var(--functional-disabled-color)) !important;
|
|
354
|
+
cursor: not-allowed !important;
|
|
355
|
+
opacity: 80% !important;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
.menu-trigger:focus {
|
|
359
|
+
box-shadow: 0 0 0 var(--functional-focus-ring-outline-width) var(--functional-focus-ring-color);
|
|
360
|
+
|
|
361
|
+
/* Needed for High Contrast mode */
|
|
362
|
+
outline:
|
|
363
|
+
var(--functional-focus-ring-outline-width) var(--functional-focus-ring-outline-style)
|
|
364
|
+
var(--functional-focus-ring-outline-color);
|
|
365
|
+
transition: box-shadow var(--functional-timing-fast) ease-out;
|
|
366
|
+
|
|
367
|
+
/* In order for the focused element's box-shadow to appear above its siblings we need to
|
|
368
|
+
establish a new stacking context: https://stackoverflow.com/a/28042700 */
|
|
369
|
+
isolation: isolate;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/* Sizes */
|
|
373
|
+
.menu-trigger-large {
|
|
374
|
+
font-size: calc(var(--functional-btn-font-size, 1rem) + 0.25rem);
|
|
375
|
+
height: 3rem;
|
|
376
|
+
line-height: 2rem;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
.menu-trigger-small {
|
|
380
|
+
font-size: calc(var(--functional-btn-font-size, 1rem) - 0.25rem);
|
|
381
|
+
height: 2rem;
|
|
382
|
+
line-height: 1rem;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
.menu-trigger-bordered {
|
|
386
|
+
--menu-item-background-color: var(--functional-menu-item-background-color, inherit);
|
|
387
|
+
|
|
388
|
+
background-color: var(--menu-item-background-color);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
.menu-trigger-rounded {
|
|
392
|
+
border-radius: var(--functional-radius);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/* TODO make this more flexible eventually */
|
|
396
|
+
.menu-icon {
|
|
397
|
+
font-family: sans-serif;
|
|
398
|
+
font-size: var(--fluid-18);
|
|
399
|
+
margin-inline-start: var(--fluid-8);
|
|
400
|
+
line-height: 1;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
:is(.btn-kebab, .btn-meatball) {
|
|
404
|
+
justify-content: space-around;
|
|
405
|
+
height: var(--fluid-24);
|
|
406
|
+
width: var(--fluid-24);
|
|
407
|
+
|
|
408
|
+
/* Rest here is supplied by btn-base and btn-blank */
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/* We use btn-blank which doesn't include this :( */
|
|
412
|
+
:is(.btn-hamburger:focus, .btn-kebab:focus, .btn-meatball:focus) {
|
|
413
|
+
box-shadow: 0 0 0 var(--functional-focus-ring-outline-width) var(--functional-focus-ring-color);
|
|
414
|
+
outline:
|
|
415
|
+
var(--functional-focus-ring-outline-width)
|
|
416
|
+
var(--functional-focus-ring-outline-style)
|
|
417
|
+
var(--functional-focus-ring-outline-color);
|
|
418
|
+
transition: box-shadow var(--functional-timing-fast) ease-out;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
.btn-hamburger,
|
|
422
|
+
.btn-kebab {
|
|
423
|
+
flex-direction: column;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
.btn-meatball {
|
|
427
|
+
flex-direction: row;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/* stylelint-disable-next-line no-duplicate-selectors */
|
|
431
|
+
.btn-meatball {
|
|
432
|
+
--block-padding: var(--functional-side-padding);
|
|
433
|
+
|
|
434
|
+
padding-block-start: var(--block-padding);
|
|
435
|
+
padding-block-end: var(--block-padding);
|
|
436
|
+
padding-inline-start: 0;
|
|
437
|
+
padding-inline-end: 0;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
.btn-hamburger {
|
|
441
|
+
--vertical-padding: 3px;
|
|
442
|
+
|
|
443
|
+
padding-block-start: var(--vertical-padding);
|
|
444
|
+
padding-block-end: var(--vertical-padding);
|
|
445
|
+
padding-inline-end: var(--fluid-2);
|
|
446
|
+
padding-inline-start: var(--fluid-2);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
.dot,
|
|
450
|
+
.bar {
|
|
451
|
+
background-color: var(--functional-dark);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
.dot {
|
|
455
|
+
width: 5px;
|
|
456
|
+
height: 5px;
|
|
457
|
+
border-radius: 50px;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
.bar {
|
|
461
|
+
width: var(--fluid-20);
|
|
462
|
+
height: var(--fluid-2);
|
|
463
|
+
margin: var(--fluid-2) 0;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
</style>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { SvelteComponent } from "svelte";
|
|
2
|
+
declare const __propDef: {
|
|
3
|
+
props: {
|
|
4
|
+
id: any;
|
|
5
|
+
type?: "simple" | "kebab" | "hamburger" | "meatball";
|
|
6
|
+
size?: "small" | "large" | "";
|
|
7
|
+
menuTitle: any;
|
|
8
|
+
menuItems?: any[];
|
|
9
|
+
isDisabled?: boolean;
|
|
10
|
+
isRounded?: boolean;
|
|
11
|
+
isBordered?: boolean;
|
|
12
|
+
isItemsRight?: boolean;
|
|
13
|
+
icon?: string;
|
|
14
|
+
onOpen: any;
|
|
15
|
+
onClose: any;
|
|
16
|
+
closeOnClickOutside?: boolean;
|
|
17
|
+
closeOnSelect?: boolean;
|
|
18
|
+
};
|
|
19
|
+
events: {
|
|
20
|
+
[evt: string]: CustomEvent<any>;
|
|
21
|
+
};
|
|
22
|
+
slots: {};
|
|
23
|
+
exports?: {} | undefined;
|
|
24
|
+
bindings?: string | undefined;
|
|
25
|
+
};
|
|
26
|
+
export type MenuProps = typeof __propDef.props;
|
|
27
|
+
export type MenuEvents = typeof __propDef.events;
|
|
28
|
+
export type MenuSlots = typeof __propDef.slots;
|
|
29
|
+
export default class Menu extends SvelteComponent<MenuProps, MenuEvents, MenuSlots> {
|
|
30
|
+
}
|
|
31
|
+
export {};
|