@neovici/cosmoz-bottom-bar 7.2.0 → 7.2.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.
|
@@ -17,6 +17,7 @@ export default html`
|
|
|
17
17
|
rgba(230, 230, 230, 0.8)
|
|
18
18
|
);
|
|
19
19
|
box-shadow: var(--cosmoz-bottom-bar-shadow, none);
|
|
20
|
+
z-index: 1;
|
|
20
21
|
|
|
21
22
|
--cosmoz-dropdown-anchor-spacing: 12px 6px;
|
|
22
23
|
--paper-button: {
|
|
@@ -29,7 +30,7 @@ export default html`
|
|
|
29
30
|
font-weight: inherit;
|
|
30
31
|
margin: 0;
|
|
31
32
|
padding: 0;
|
|
32
|
-
}
|
|
33
|
+
};
|
|
33
34
|
}
|
|
34
35
|
:host([force-open]) {
|
|
35
36
|
transition: none;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neovici/cosmoz-bottom-bar",
|
|
3
|
-
"version": "7.2.
|
|
3
|
+
"version": "7.2.2",
|
|
4
4
|
"description": "A responsive bottom-bar that can house buttons/actions and a menu for the buttons that won't fit the available width.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"polymer",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
},
|
|
27
27
|
"files": [
|
|
28
28
|
"cosmoz-*.js",
|
|
29
|
-
"
|
|
29
|
+
"src/**/*.js"
|
|
30
30
|
],
|
|
31
31
|
"scripts": {
|
|
32
32
|
"lint": "eslint --cache --ext .js .",
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/* eslint-disable max-len */
|
|
2
|
+
/* eslint-disable max-lines */
|
|
3
|
+
import { html } from 'lit-html';
|
|
4
|
+
import { component, useLayoutEffect } from '@pionjs/pion';
|
|
5
|
+
import { useHost } from '@neovici/cosmoz-utils/hooks/use-host';
|
|
6
|
+
import { style } from './cosmoz-bottom-bar-next.style.js';
|
|
7
|
+
import { toggleSize } from '@neovici/cosmoz-collapse/toggle';
|
|
8
|
+
import { sheet } from '@neovici/cosmoz-utils';
|
|
9
|
+
import '@neovici/cosmoz-dropdown';
|
|
10
|
+
|
|
11
|
+
const BOTTOM_BAR_TOOLBAR_SLOT = 'bottom-bar-toolbar';
|
|
12
|
+
const BOTTOM_BAR_MENU_SLOT = 'bottom-bar-menu';
|
|
13
|
+
|
|
14
|
+
const _moveElement = (element, toToolbar) => {
|
|
15
|
+
const slot = toToolbar ? BOTTOM_BAR_TOOLBAR_SLOT : BOTTOM_BAR_MENU_SLOT;
|
|
16
|
+
const tabindex = '0';
|
|
17
|
+
|
|
18
|
+
element.setAttribute('slot', slot);
|
|
19
|
+
element.setAttribute('tabindex', tabindex);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const _isActionNode = (node) => {
|
|
23
|
+
return (
|
|
24
|
+
node.nodeType === Node.ELEMENT_NODE &&
|
|
25
|
+
node.getAttribute('slot') !== 'info' &&
|
|
26
|
+
node.tagName !== 'TEMPLATE' &&
|
|
27
|
+
node.tagName !== 'STYLE' &&
|
|
28
|
+
node.tagName !== 'DOM-REPEAT' &&
|
|
29
|
+
node.tagName !== 'DOM-IF' &&
|
|
30
|
+
node.getAttribute('slot') !== 'extra'
|
|
31
|
+
);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const getFlattenedNodes = (element) => {
|
|
35
|
+
const childNodes = [...element.childNodes];
|
|
36
|
+
|
|
37
|
+
for (let i = 0; i < element.childNodes.length; i++) {
|
|
38
|
+
const node = element.childNodes[i];
|
|
39
|
+
if (node.tagName === 'SLOT') {
|
|
40
|
+
// remove current slot element
|
|
41
|
+
childNodes.splice(i, 1);
|
|
42
|
+
|
|
43
|
+
// append slot elements to the current index
|
|
44
|
+
const slotElements = node.assignedElements({ flatten: true });
|
|
45
|
+
for (let j = 0; j < slotElements.length; j++) {
|
|
46
|
+
const slotElement = slotElements[j];
|
|
47
|
+
|
|
48
|
+
childNodes.splice(i + j, 0, slotElement);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return childNodes;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const _getElements = (host) => {
|
|
57
|
+
const elements = getFlattenedNodes(host)
|
|
58
|
+
.filter(_isActionNode)
|
|
59
|
+
.filter((element) => !element.hidden)
|
|
60
|
+
.sort((a, b) => (a.dataset.index ?? 0) - (b.dataset.index ?? 0));
|
|
61
|
+
|
|
62
|
+
if (elements.length === 0) {
|
|
63
|
+
return elements;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const topPriorityAction = elements.reduce(
|
|
67
|
+
(top, element) => {
|
|
68
|
+
return parseInt(top.dataset.priority ?? 0, 10) >=
|
|
69
|
+
parseInt(element.dataset.priority ?? 0, 10)
|
|
70
|
+
? top
|
|
71
|
+
: element;
|
|
72
|
+
},
|
|
73
|
+
{ dataset: { priority: '-1000' } },
|
|
74
|
+
[],
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
return [
|
|
78
|
+
topPriorityAction,
|
|
79
|
+
...elements.filter((e) => e !== topPriorityAction),
|
|
80
|
+
];
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Layout the actions available as buttons or menu items
|
|
85
|
+
*
|
|
86
|
+
* If the window is resizing down, just make sure that all buttons fits, and if not,
|
|
87
|
+
* move one to menu and call itself async (to allow re-rendering) and see if we fit.
|
|
88
|
+
* Repeat until the button fits or no buttons are left.
|
|
89
|
+
*
|
|
90
|
+
* If the window is sizing up, try to place a menu item out as a button, call itself
|
|
91
|
+
* async (to allow re-rendering) and see if we fit - if we don't, remove the button again.
|
|
92
|
+
*
|
|
93
|
+
* We also need to keep track of `_scalingUp` between calls since the resize might fire
|
|
94
|
+
* a lot of events, and we don't want to be starting multiple "calculation processes"
|
|
95
|
+
* since this will result in an infinite loop.
|
|
96
|
+
*
|
|
97
|
+
* The actual layouting of actions will be performed by adding or removing the 'button'
|
|
98
|
+
* attribute from the action, which will cause it to match different content insertion
|
|
99
|
+
* points.
|
|
100
|
+
*
|
|
101
|
+
* @param {*} host The current element
|
|
102
|
+
* @param {*} maxToolbarItems Maximum items for the toolbar
|
|
103
|
+
* @returns {void}
|
|
104
|
+
*/
|
|
105
|
+
const _layoutActions = (host, maxToolbarItems) => {
|
|
106
|
+
// eslint-disable-line max-statements
|
|
107
|
+
const elements = _getElements(host);
|
|
108
|
+
const hasActions = elements.length > 0;
|
|
109
|
+
|
|
110
|
+
if (!hasActions) {
|
|
111
|
+
// No need to render if we don't have any actions
|
|
112
|
+
return host.toggleAttribute('has-menu-items', false);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const toolbarElements = elements.slice(0, maxToolbarItems);
|
|
116
|
+
const menuElements = elements.slice(toolbarElements.length);
|
|
117
|
+
|
|
118
|
+
toolbarElements.forEach((el) => _moveElement(el, true));
|
|
119
|
+
menuElements.forEach((el) => _moveElement(el));
|
|
120
|
+
host.toggleAttribute('has-menu-items', menuElements.length > 0);
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* `<cosmoz-bottom-bar>` is a horizontal responsive bottom toolbar containing items that
|
|
125
|
+
* can be used for actions.
|
|
126
|
+
*
|
|
127
|
+
* The items placed inside the `cosmoz-bottom-bar` are distributed into the toolbar in a horizontal container.
|
|
128
|
+
* If the items do not fit the available width, those that do not fit are placed in a dropdown
|
|
129
|
+
* menu triggered by a button in the toolbar.
|
|
130
|
+
* The class specified by the property `toolbarClass` (default `cosmoz-bottom-bar-toolbar`)
|
|
131
|
+
* is applied to items distributed to the toolbar.
|
|
132
|
+
* The class specified in the property `menuClass` (default `cosmoz-bottom-bar-menu`)
|
|
133
|
+
* is applied to items distributed to the menu.
|
|
134
|
+
*
|
|
135
|
+
* ### Usage
|
|
136
|
+
*
|
|
137
|
+
* See demo for example usage
|
|
138
|
+
*
|
|
139
|
+
* @element cosmoz-bottom-bar
|
|
140
|
+
* @demo demo/bottom-bar-next.html Basic Demo
|
|
141
|
+
*/
|
|
142
|
+
// eslint-disable-next-line max-statements
|
|
143
|
+
const CosmozBottomBar = ({ active = false, maxToolbarItems = 1 }) => {
|
|
144
|
+
const host = useHost();
|
|
145
|
+
|
|
146
|
+
const toggle = toggleSize('height');
|
|
147
|
+
|
|
148
|
+
useLayoutEffect(() => {
|
|
149
|
+
toggle(host, active);
|
|
150
|
+
}, [active]);
|
|
151
|
+
|
|
152
|
+
const slotChangeHandler = () => {
|
|
153
|
+
_layoutActions(host, maxToolbarItems);
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
return html`<div id="bar" part="bar">
|
|
157
|
+
<div id="info"><slot name="info"></slot></div>
|
|
158
|
+
<slot
|
|
159
|
+
id="bottomBarToolbar"
|
|
160
|
+
name="bottom-bar-toolbar"
|
|
161
|
+
@slotchange=${slotChangeHandler}
|
|
162
|
+
></slot>
|
|
163
|
+
<cosmoz-dropdown-menu id="dropdown">
|
|
164
|
+
<svg
|
|
165
|
+
slot="button"
|
|
166
|
+
width="4"
|
|
167
|
+
height="16"
|
|
168
|
+
viewBox="0 0 4 16"
|
|
169
|
+
fill="none"
|
|
170
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
171
|
+
>
|
|
172
|
+
<path
|
|
173
|
+
fill-rule="evenodd"
|
|
174
|
+
clip-rule="evenodd"
|
|
175
|
+
d="M1.50996e-07 2C1.02714e-07 3.10457 0.89543 4 2 4C3.10457 4 4 3.10457 4 2C4 0.89543 3.10457 -3.91405e-08 2 -8.74228e-08C0.895431 -1.35705e-07 1.99278e-07 0.89543 1.50996e-07 2Z"
|
|
176
|
+
fill="white"
|
|
177
|
+
/>
|
|
178
|
+
<path
|
|
179
|
+
fill-rule="evenodd"
|
|
180
|
+
clip-rule="evenodd"
|
|
181
|
+
d="M1.50996e-07 8C1.02714e-07 9.10457 0.89543 10 2 10C3.10457 10 4 9.10457 4 8C4 6.89543 3.10457 6 2 6C0.895431 6 1.99278e-07 6.89543 1.50996e-07 8Z"
|
|
182
|
+
fill="white"
|
|
183
|
+
/>
|
|
184
|
+
<path
|
|
185
|
+
fill-rule="evenodd"
|
|
186
|
+
clip-rule="evenodd"
|
|
187
|
+
d="M1.50996e-07 14C1.02714e-07 15.1046 0.89543 16 2 16C3.10457 16 4 15.1046 4 14C4 12.8954 3.10457 12 2 12C0.895431 12 1.99278e-07 12.8954 1.50996e-07 14Z"
|
|
188
|
+
fill="white"
|
|
189
|
+
/>
|
|
190
|
+
</svg>
|
|
191
|
+
<slot id="bottomBarMenu" name="bottom-bar-menu"></slot>
|
|
192
|
+
</cosmoz-dropdown-menu>
|
|
193
|
+
<slot name="extra" id="extraSlot"></slot>
|
|
194
|
+
</div>
|
|
195
|
+
<div hidden style="display:none">
|
|
196
|
+
<slot id="content" @slotchange=${slotChangeHandler}></slot>
|
|
197
|
+
</div>`;
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
export default CosmozBottomBar;
|
|
201
|
+
|
|
202
|
+
customElements.define(
|
|
203
|
+
'cosmoz-bottom-bar-next',
|
|
204
|
+
component(CosmozBottomBar, {
|
|
205
|
+
observedAttributes: ['active'],
|
|
206
|
+
styleSheets: [sheet(style)],
|
|
207
|
+
}),
|
|
208
|
+
);
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/* eslint-disable max-len */
|
|
2
|
+
import { css } from '@neovici/cosmoz-utils';
|
|
3
|
+
|
|
4
|
+
export const style = css`
|
|
5
|
+
:host {
|
|
6
|
+
display: block;
|
|
7
|
+
overflow: hidden;
|
|
8
|
+
bottom: 0;
|
|
9
|
+
left: 0;
|
|
10
|
+
width: 100%;
|
|
11
|
+
max-width: 100%; /* Firefox fix */
|
|
12
|
+
background-color: inherit;
|
|
13
|
+
transition: max-height 0.3s ease;
|
|
14
|
+
flex: none;
|
|
15
|
+
background-color: var(
|
|
16
|
+
--cosmoz-bottom-bar-bg-color,
|
|
17
|
+
rgba(230, 230, 230, 0.8)
|
|
18
|
+
);
|
|
19
|
+
box-shadow: var(--cosmoz-bottom-bar-shadow, none);
|
|
20
|
+
|
|
21
|
+
--cosmoz-dropdown-anchor-spacing: 12px 6px;
|
|
22
|
+
--paper-button: {
|
|
23
|
+
background-color: inherit;
|
|
24
|
+
text-transform: none;
|
|
25
|
+
border: 0;
|
|
26
|
+
border-radius: 0;
|
|
27
|
+
font-size: inherit;
|
|
28
|
+
color: inherit;
|
|
29
|
+
font-weight: inherit;
|
|
30
|
+
margin: 0;
|
|
31
|
+
padding: 0;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
:host([force-open]) {
|
|
35
|
+
transition: none;
|
|
36
|
+
}
|
|
37
|
+
[hidden],
|
|
38
|
+
::slotted([hidden]) {
|
|
39
|
+
display: none !important;
|
|
40
|
+
}
|
|
41
|
+
#bar {
|
|
42
|
+
height: 64px;
|
|
43
|
+
padding: 0 3%;
|
|
44
|
+
display: flex;
|
|
45
|
+
align-items: center;
|
|
46
|
+
}
|
|
47
|
+
#info {
|
|
48
|
+
min-width: 5px;
|
|
49
|
+
padding-right: 3%;
|
|
50
|
+
margin-right: auto;
|
|
51
|
+
white-space: nowrap;
|
|
52
|
+
}
|
|
53
|
+
#bottomBarToolbar::slotted(:not(slot)) {
|
|
54
|
+
margin: 0 0.29em;
|
|
55
|
+
min-width: 40px;
|
|
56
|
+
min-height: 40px;
|
|
57
|
+
text-overflow: ellipsis;
|
|
58
|
+
white-space: nowrap;
|
|
59
|
+
background: var(
|
|
60
|
+
--cosmoz-bottom-bar-button-bg-color,
|
|
61
|
+
var(--cosmoz-button-bg-color, #101010)
|
|
62
|
+
);
|
|
63
|
+
color: var(
|
|
64
|
+
--cosmoz-bottom-bar-button-color,
|
|
65
|
+
var(--cosmoz-button-color, #fff)
|
|
66
|
+
);
|
|
67
|
+
border-radius: 6px;
|
|
68
|
+
padding: 0 18px;
|
|
69
|
+
font-size: 14px;
|
|
70
|
+
font-weight: 500;
|
|
71
|
+
line-height: 40px;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
#bottomBarToolbar::slotted(:not(slot)[disabled]) {
|
|
75
|
+
opacity: var(--cosmoz-button-disabled-opacity, 0.15);
|
|
76
|
+
pointer-events: none;
|
|
77
|
+
}
|
|
78
|
+
#bottomBarToolbar::slotted(:not(slot):hover) {
|
|
79
|
+
background: var(
|
|
80
|
+
--cosmoz-bottom-bar-button-hover-bg-color,
|
|
81
|
+
var(--cosmoz-button-hover-bg-color, #3a3f44)
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
#dropdown::part(content) {
|
|
85
|
+
max-width: 300px;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
:host([hide-actions]) #bottomBarToolbar,
|
|
89
|
+
:host([hide-actions]) #bottomBarMenu,
|
|
90
|
+
:host([hide-actions]) #dropdown {
|
|
91
|
+
display: none;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
:host(:not([has-menu-items])) cosmoz-dropdown-menu {
|
|
95
|
+
display: none;
|
|
96
|
+
}
|
|
97
|
+
`;
|