@neovici/cosmoz-bottom-bar 8.1.1 → 9.0.0

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.
@@ -0,0 +1,28 @@
1
+ import '@neovici/cosmoz-dropdown';
2
+ export declare const openMenu: unique symbol;
3
+ /**
4
+ * `<cosmoz-bottom-bar>` is a horizontal responsive bottom toolbar containing items that
5
+ * can be used for actions.
6
+ *
7
+ * The items placed inside the `cosmoz-bottom-bar` are distributed into the toolbar in a horizontal container.
8
+ * If the items do not fit the available width, those that do not fit are placed in a dropdown
9
+ * menu triggered by a button in the toolbar.
10
+ * The class specified by the property `toolbarClass` (default `cosmoz-bottom-bar-toolbar`)
11
+ * is applied to items distributed to the toolbar.
12
+ * The class specified in the property `menuClass` (default `cosmoz-bottom-bar-menu`)
13
+ * is applied to items distributed to the menu.
14
+ *
15
+ * ### Usage
16
+ *
17
+ * See demo for example usage
18
+ *
19
+ * @element cosmoz-bottom-bar
20
+ * @demo demo/bottom-bar-next.html Basic Demo
21
+ */
22
+ type Host = HTMLElement & {
23
+ active?: boolean;
24
+ maxToolbarItems?: number;
25
+ };
26
+ declare const CosmozBottomBar: (host: Host) => import("lit-html").TemplateResult<1>;
27
+ export default CosmozBottomBar;
28
+ export declare const bottomBarSlots: import("lit-html").TemplateResult<1>, bottomBarSlotsPolymer: HTMLTemplateElement;
@@ -0,0 +1,233 @@
1
+ /* eslint-disable max-len */
2
+ import { html } from 'lit-html';
3
+ import { map } from 'lit-html/directives/map.js';
4
+ import { html as polymerHtml } from '@polymer/polymer/polymer-element.js';
5
+ import { component, css, useEffect, useLayoutEffect, useMemo, useState, } from '@pionjs/pion';
6
+ import { toggleSize } from '@neovici/cosmoz-collapse/toggle';
7
+ import { useActivity } from '@neovici/cosmoz-utils/keybindings/use-activity';
8
+ import '@neovici/cosmoz-dropdown';
9
+ import overflow from './overflow';
10
+ const style = css `
11
+ :host {
12
+ display: block;
13
+ overflow: hidden;
14
+ bottom: 0;
15
+ left: 0;
16
+ width: 100%;
17
+ max-width: 100%; /* Firefox fix */
18
+ background-color: inherit;
19
+ transition: max-height 0.3s ease;
20
+ flex: none;
21
+ background-color: var(
22
+ --cosmoz-bottom-bar-bg-color,
23
+ rgba(230, 230, 230, 0.8)
24
+ );
25
+ box-shadow: var(--cosmoz-bottom-bar-shadow, none);
26
+ z-index: 1;
27
+
28
+ --cosmoz-dropdown-anchor-spacing: 12px 6px;
29
+ --paper-button: {
30
+ background-color: inherit;
31
+ text-transform: none;
32
+ border: 0;
33
+ border-radius: 0;
34
+ font-size: inherit;
35
+ color: inherit;
36
+ font-weight: inherit;
37
+ margin: 0;
38
+ padding: 0;
39
+ };
40
+ }
41
+
42
+ :host([force-open]) {
43
+ transition: none;
44
+ }
45
+
46
+ [hidden],
47
+ ::slotted([hidden]) {
48
+ display: none !important;
49
+ }
50
+
51
+ #bar {
52
+ height: 64px;
53
+ padding: 0 3%;
54
+ display: flex;
55
+ align-items: center;
56
+ }
57
+
58
+ #info {
59
+ flex: 0 0 auto;
60
+ padding-right: 3%;
61
+ white-space: nowrap;
62
+ }
63
+
64
+ #buttonContainer {
65
+ display: flex;
66
+ flex: 1 1 auto;
67
+ overflow: hidden;
68
+ flex-wrap: wrap;
69
+ justify-content: flex-start;
70
+ flex-direction: row-reverse;
71
+ position: relative;
72
+ margin: 0 8px;
73
+ min-width: 0;
74
+ max-height: 40px;
75
+ }
76
+
77
+ #bottomBarToolbar::slotted(:not(slot):not([unstyled])) {
78
+ margin: 0 0.29em;
79
+ min-width: 40px;
80
+ min-height: 40px;
81
+ text-overflow: ellipsis;
82
+ white-space: nowrap;
83
+ background: var(
84
+ --cosmoz-bottom-bar-button-bg-color,
85
+ var(--cosmoz-button-bg-color, #101010)
86
+ );
87
+ color: var(
88
+ --cosmoz-bottom-bar-button-color,
89
+ var(--cosmoz-button-color, #fff)
90
+ );
91
+ border-radius: 6px;
92
+ padding: 0 18px;
93
+ font-size: 14px;
94
+ font-weight: 500;
95
+ line-height: 40px;
96
+ overflow: hidden;
97
+ flex: 0 0 auto;
98
+ visibility: hidden;
99
+ }
100
+
101
+ #bottomBarToolbar::slotted(:not(slot)[disabled]) {
102
+ opacity: var(--cosmoz-button-disabled-opacity, 0.15);
103
+ pointer-events: none;
104
+ }
105
+
106
+ #bottomBarToolbar::slotted(:not(slot):hover) {
107
+ background: var(
108
+ --cosmoz-bottom-bar-button-hover-bg-color,
109
+ var(--cosmoz-button-hover-bg-color, #3a3f44)
110
+ );
111
+ }
112
+
113
+ #dropdown::part(content) {
114
+ max-width: 300px;
115
+ }
116
+
117
+ :host([hide-actions]) #bottomBarToolbar,
118
+ :host([hide-actions]) #bottomBarMenu,
119
+ :host([hide-actions]) #dropdown {
120
+ display: none;
121
+ }
122
+
123
+ :host(:not([has-menu-items])) cosmoz-dropdown-menu {
124
+ display: none;
125
+ }
126
+ `;
127
+ export const openMenu = Symbol('openMenu');
128
+ const openActionsMenu = (host) => {
129
+ const dropdown = host.shadowRoot?.querySelector('#dropdown');
130
+ if (!dropdown || dropdown.hasAttribute('hidden'))
131
+ return;
132
+ //TODO: Clean up when open function is implemented for cosmoz-dropdown-menu
133
+ const cosmozDropdown = dropdown.shadowRoot?.querySelector('cosmoz-dropdown'), button = cosmozDropdown?.shadowRoot?.querySelector('#dropdownButton');
134
+ button?.click();
135
+ };
136
+ const useMenuButtons = (host) => {
137
+ const [buttonStates, setButtonStates] = useState({
138
+ visible: new Set(),
139
+ overflowing: new Set(),
140
+ });
141
+ const allButtons = useMemo(() => [...buttonStates.visible, ...buttonStates.overflowing], [buttonStates]);
142
+ const processedButtons = useMemo(() => allButtons
143
+ .map((btn) => ({
144
+ element: btn,
145
+ priority: Number(btn.dataset.priority ?? 0),
146
+ text: btn.textContent?.trim() || '',
147
+ }))
148
+ .toSorted((a, b) => b.priority - a.priority), [allButtons]);
149
+ const { maxToolbarItems = 1 } = host;
150
+ const toolbarLimit = Math.min(maxToolbarItems, buttonStates.visible.size >= 0
151
+ ? buttonStates.visible.size
152
+ : allButtons.length);
153
+ useEffect(() => {
154
+ processedButtons.forEach(({ element, priority }, i) => {
155
+ const isVisible = i < toolbarLimit;
156
+ element.style.visibility = isVisible ? 'visible' : 'hidden';
157
+ element.style.order = String(-priority);
158
+ });
159
+ }, [processedButtons, toolbarLimit]);
160
+ const menuButtons = useMemo(() => processedButtons
161
+ .slice(toolbarLimit)
162
+ .sort((a, b) => b.element.compareDocumentPosition(a.element) -
163
+ a.element.compareDocumentPosition(b.element)), [processedButtons, toolbarLimit]);
164
+ useEffect(() => {
165
+ host.toggleAttribute('has-menu-items', menuButtons.length > 0);
166
+ }, [menuButtons.length]);
167
+ return { setButtonStates, menuButtons };
168
+ };
169
+ const CosmozBottomBar = (host) => {
170
+ const { active = false } = host;
171
+ useActivity({
172
+ activity: openMenu,
173
+ callback: () => openActionsMenu(host),
174
+ check: () => active && !host.hasAttribute('hide-actions'),
175
+ element: () => host.shadowRoot?.querySelector('#dropdown'),
176
+ }, [active]);
177
+ const { setButtonStates, menuButtons } = useMenuButtons(host);
178
+ const toggle = useMemo(() => toggleSize('height'), []);
179
+ useLayoutEffect(() => {
180
+ toggle(host, active);
181
+ }, [active]);
182
+ return html `<div id="bar" part="bar">
183
+ <div id="info" part="info"><slot name="info"></slot></div>
184
+ <div id="buttonContainer">
185
+ <slot id="bottomBarToolbar" ${overflow(setButtonStates)}></slot>
186
+ </div>
187
+
188
+ <cosmoz-dropdown-menu id="dropdown">
189
+ <svg
190
+ slot="button"
191
+ width="4"
192
+ height="16"
193
+ viewBox="0 0 4 16"
194
+ fill="none"
195
+ xmlns="http://www.w3.org/2000/svg"
196
+ >
197
+ <path
198
+ fill-rule="evenodd"
199
+ clip-rule="evenodd"
200
+ 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"
201
+ fill="white"
202
+ />
203
+ <path
204
+ fill-rule="evenodd"
205
+ clip-rule="evenodd"
206
+ 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"
207
+ fill="white"
208
+ />
209
+ <path
210
+ fill-rule="evenodd"
211
+ clip-rule="evenodd"
212
+ 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"
213
+ fill="white"
214
+ />
215
+ </svg>
216
+ ${map(menuButtons, (menuButton) => html `
217
+ <paper-button @click=${() => menuButton.element.click()}
218
+ >${menuButton.text}</paper-button
219
+ >
220
+ `)}
221
+ </cosmoz-dropdown-menu>
222
+ <slot name="extra" id="extraSlot"></slot>
223
+ </div>`;
224
+ };
225
+ export default CosmozBottomBar;
226
+ customElements.define('cosmoz-bottom-bar', component(CosmozBottomBar, {
227
+ observedAttributes: ['active', 'max-toolbar-items'],
228
+ styleSheets: [style],
229
+ }));
230
+ const tmplt = `
231
+ <slot name="extra" slot="extra"></slot>
232
+ `;
233
+ export const bottomBarSlots = html(Object.assign([tmplt], { raw: [tmplt] })), bottomBarSlotsPolymer = polymerHtml(Object.assign([tmplt], { raw: [tmplt] }));
@@ -0,0 +1 @@
1
+ export * from './cosmoz-bottom-bar';
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export * from './cosmoz-bottom-bar';
@@ -0,0 +1,19 @@
1
+ import { AttributePart } from 'lit-html';
2
+ import { AsyncDirective } from 'lit-html/async-directive.js';
3
+ import { DirectiveResult } from 'lit-html/directive.js';
4
+ type OnOverflow = (opts: {
5
+ visible: Set<HTMLElement>;
6
+ overflowing: Set<HTMLElement>;
7
+ }) => void;
8
+ declare class OverflowDirective extends AsyncDirective {
9
+ observer?: IntersectionObserver;
10
+ slot?: HTMLSlotElement;
11
+ cleanup?: () => void;
12
+ render(): symbol;
13
+ update(part: AttributePart, [onOverflow]: [OnOverflow]): symbol;
14
+ }
15
+ interface Overflow {
16
+ (onOverflow: OnOverflow): DirectiveResult<typeof OverflowDirective>;
17
+ }
18
+ declare const overflow: Overflow;
19
+ export default overflow;
@@ -0,0 +1,68 @@
1
+ import { noChange } from 'lit-html';
2
+ import { AsyncDirective } from 'lit-html/async-directive.js';
3
+ import { directive } from 'lit-html/directive.js';
4
+ function isEntryHidden(el) {
5
+ return el.boundingClientRect.height === 0;
6
+ }
7
+ const check = (part) => {
8
+ if (part.element.tagName !== 'SLOT') {
9
+ throw new Error('Overflow directive must be used on a slot element');
10
+ }
11
+ };
12
+ const setupObserver = (slot, onOverflow) => {
13
+ const visible = new Set();
14
+ const overflowing = new Set();
15
+ const observer = new IntersectionObserver((entries) => {
16
+ entries.forEach((entry) => {
17
+ const el = entry.target;
18
+ if (entry.intersectionRatio === 1) {
19
+ visible.add(el);
20
+ overflowing.delete(el);
21
+ }
22
+ else if (isEntryHidden(entry)) {
23
+ visible.delete(el);
24
+ overflowing.delete(el);
25
+ }
26
+ else {
27
+ visible.delete(el);
28
+ overflowing.add(el);
29
+ }
30
+ });
31
+ onOverflow({ visible, overflowing });
32
+ }, { root: slot.parentElement, threshold: 1 });
33
+ const observe = () => {
34
+ const elements = Array.from(slot.assignedElements());
35
+ for (const c of elements) {
36
+ observer.observe(c);
37
+ }
38
+ };
39
+ observe();
40
+ slot.addEventListener('slotchange', observe);
41
+ return () => {
42
+ slot.removeEventListener('slotchange', observe);
43
+ observer.disconnect();
44
+ };
45
+ };
46
+ class OverflowDirective extends AsyncDirective {
47
+ observer;
48
+ slot;
49
+ cleanup;
50
+ render() {
51
+ return noChange;
52
+ }
53
+ update(part, [onOverflow]) {
54
+ check(part);
55
+ const hasChanged = this.slot !== part.element;
56
+ if (hasChanged) {
57
+ if (this.cleanup) {
58
+ this.cleanup();
59
+ this.cleanup = undefined;
60
+ }
61
+ this.slot = part.element;
62
+ this.cleanup = setupObserver(this.slot, onOverflow);
63
+ }
64
+ return noChange;
65
+ }
66
+ }
67
+ const overflow = directive(OverflowDirective);
68
+ export default overflow;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neovici/cosmoz-bottom-bar",
3
- "version": "8.1.1",
3
+ "version": "9.0.0",
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",
@@ -16,20 +16,17 @@
16
16
  },
17
17
  "license": "Apache-2.0",
18
18
  "author": "Neovici Development",
19
- "main": "cosmoz-bottom-bar.js",
19
+ "main": "dist/index.js",
20
20
  "exports": {
21
- ".": "./cosmoz-bottom-bar.js",
22
- "./next": "./src/cosmoz-bottom-bar-next.js"
23
- },
24
- "directories": {
25
- "test": "test"
21
+ ".": "./dist/index.js"
26
22
  },
27
23
  "files": [
28
- "cosmoz-*.js",
29
- "src/**/*.js"
24
+ "dist/*",
25
+ "types"
30
26
  ],
31
27
  "scripts": {
32
- "lint": "eslint --cache --ext .js .",
28
+ "lint": "tsc && eslint --cache .",
29
+ "build": "tsc -p tsconfig.build.json",
33
30
  "start": "npm run storybook:start",
34
31
  "test": "wtr --coverage",
35
32
  "test:watch": "wtr --watch",
@@ -72,14 +69,14 @@
72
69
  "@polymer/paper-toggle-button": "^3.0.0",
73
70
  "@semantic-release/changelog": "^6.0.0",
74
71
  "@semantic-release/git": "^10.0.0",
75
- "@storybook/addon-essentials": "^7.0.0",
72
+ "@storybook/addon-essentials": "^8.6.9",
76
73
  "@storybook/addon-links": "^7.0.0",
77
- "@storybook/builder-vite": "7.6.17",
74
+ "@storybook/builder-vite": "8.6.9",
78
75
  "@storybook/storybook-deployer": "^2.8.5",
79
- "@storybook/web-components": "7.6.17",
76
+ "@storybook/web-components": "8.6.9",
80
77
  "@types/mocha": "^10.0.6",
81
- "@web/storybook-builder": "^0.1.9",
82
- "@web/storybook-framework-web-components": "^0.1.1",
78
+ "@web/storybook-builder": "^0.2.1",
79
+ "@web/storybook-framework-web-components": "^0.2.0",
83
80
  "@webcomponents/webcomponentsjs": "^2.5.0",
84
81
  "esbuild": "^0.25.0",
85
82
  "eslint": "^8.57.1",
@@ -88,7 +85,7 @@
88
85
  "rollup-plugin-esbuild": "^6.1.1",
89
86
  "semantic-release": "^24.0.0",
90
87
  "sinon": "^19.0.0",
91
- "storybook": "^7.6.17",
88
+ "storybook": "^8.6.9",
92
89
  "typescript": "^5.0.0"
93
90
  }
94
91
  }
@@ -1,137 +0,0 @@
1
- /* eslint-disable max-len */
2
- import '@neovici/cosmoz-dropdown';
3
- import { html } from '@polymer/polymer/lib/utils/html-tag';
4
-
5
- export default html`
6
- <style>
7
- :host {
8
- display: block;
9
- bottom: 0;
10
- left: 0;
11
- width: 100%;
12
- max-width: 100%; /* Firefox fix */
13
- background-color: inherit;
14
- transition: max-height 0.3s ease;
15
- flex: none;
16
- background-color: var(
17
- --cosmoz-bottom-bar-bg-color,
18
- rgba(230, 230, 230, 0.8)
19
- );
20
- box-shadow: var(--cosmoz-bottom-bar-shadow, none);
21
- z-index: 1;
22
-
23
- --cosmoz-dropdown-anchor-spacing: 12px 6px;
24
- --paper-button: {
25
- background-color: inherit;
26
- text-transform: none;
27
- border: 0;
28
- border-radius: 0;
29
- font-size: inherit;
30
- color: inherit;
31
- font-weight: inherit;
32
- margin: 0;
33
- padding: 0;
34
- };
35
- }
36
- :host([force-open]) {
37
- transition: none;
38
- }
39
- [hidden],
40
- ::slotted([hidden]) {
41
- display: none !important;
42
- }
43
- #bar {
44
- padding: 0 3%;
45
- display: flex;
46
- align-items: center;
47
- }
48
- #info {
49
- min-width: 5px;
50
- padding-right: 3%;
51
- margin-right: auto;
52
- white-space: nowrap;
53
- }
54
- #bottomBarToolbar::slotted(:not(slot):not([unstyled])) {
55
- margin: 0 0.29em;
56
- min-width: 40px;
57
- min-height: 40px;
58
- text-overflow: ellipsis;
59
- white-space: nowrap;
60
- background: var(
61
- --cosmoz-bottom-bar-button-bg-color,
62
- var(--cosmoz-button-bg-color, #101010)
63
- );
64
- color: var(
65
- --cosmoz-bottom-bar-button-color,
66
- var(--cosmoz-button-color, #fff)
67
- );
68
- border-radius: 6px;
69
- padding: 0 18px;
70
- font-size: 14px;
71
- font-weight: 500;
72
- line-height: 40px;
73
- }
74
-
75
- #bottomBarToolbar::slotted(:not(slot)[disabled]) {
76
- opacity: var(--cosmoz-button-disabled-opacity, 0.15);
77
- pointer-events: none;
78
- }
79
- #bottomBarToolbar::slotted(:not(slot):hover) {
80
- background: var(
81
- --cosmoz-bottom-bar-button-hover-bg-color,
82
- var(--cosmoz-button-hover-bg-color, #3a3f44)
83
- );
84
- }
85
- #dropdown::part(content) {
86
- max-width: 300px;
87
- }
88
-
89
- :host([hide-actions]) #bottomBarToolbar,
90
- :host([hide-actions]) #bottomBarMenu,
91
- :host([hide-actions]) #dropdown {
92
- display: none;
93
- }
94
- </style>
95
- <div id="bar" style$="[[ _getHeightStyle(computedBarHeight) ]]" part="bar">
96
- <div id="info" part="info"><slot name="info"></slot></div>
97
- <slot id="bottomBarToolbar" name="bottom-bar-toolbar"></slot>
98
- <cosmoz-dropdown-menu
99
- id="dropdown"
100
- hidden="[[ !hasMenuItems ]]"
101
- placement="[[ topPlacement ]]"
102
- >
103
- <svg
104
- slot="button"
105
- width="4"
106
- height="16"
107
- viewBox="0 0 4 16"
108
- fill="none"
109
- xmlns="http://www.w3.org/2000/svg"
110
- >
111
- <path
112
- fill-rule="evenodd"
113
- clip-rule="evenodd"
114
- 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"
115
- fill="white"
116
- />
117
- <path
118
- fill-rule="evenodd"
119
- clip-rule="evenodd"
120
- 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"
121
- fill="white"
122
- />
123
- <path
124
- fill-rule="evenodd"
125
- clip-rule="evenodd"
126
- 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"
127
- fill="white"
128
- />
129
- </svg>
130
- <slot id="bottomBarMenu" name="bottom-bar-menu"></slot>
131
- </cosmoz-dropdown-menu>
132
- <slot name="extra" id="extraSlot"></slot>
133
- </div>
134
- <div hidden style="display:none">
135
- <slot id="content"></slot>
136
- </div>
137
- `;
@@ -1,435 +0,0 @@
1
- import {
2
- PolymerElement,
3
- html as polymerHtml,
4
- } from '@polymer/polymer/polymer-element.js';
5
- import { FlattenedNodesObserver } from '@polymer/polymer/lib/utils/flattened-nodes-observer';
6
- import { Debouncer } from '@polymer/polymer/lib/utils/debounce.js';
7
- import { timeOut } from '@polymer/polymer/lib/utils/async';
8
- import { html } from 'lit-html';
9
- import { hauntedPolymer } from '@neovici/cosmoz-utils';
10
- import { useActivity } from '@neovici/cosmoz-utils/keybindings/use-activity';
11
- import template from './cosmoz-bottom-bar.html.js';
12
-
13
- const BOTTOM_BAR_TOOLBAR_SLOT = 'bottom-bar-toolbar',
14
- BOTTOM_BAR_MENU_SLOT = 'bottom-bar-menu',
15
- rendered = Symbol('rendered');
16
-
17
- export const openMenu = Symbol('openMenu');
18
-
19
- const openActionsMenu = (host) => {
20
- const dropdown = host.$.dropdown;
21
-
22
- if (dropdown.hasAttribute('hidden')) return;
23
-
24
- //TODO: Clean up when open function is implemented for cosmoz-dropdown-menu
25
- dropdown.shadowRoot
26
- .querySelector('cosmoz-dropdown')
27
- .shadowRoot.getElementById('dropdownButton')
28
- .click();
29
- },
30
- useBottomBar = (host) => {
31
- useActivity(
32
- {
33
- activity: openMenu,
34
- callback: () => openActionsMenu(host),
35
- check: () => host.active && !host.hasAttribute('hide-actions'),
36
- element: () => host.$.dropdown,
37
- },
38
- [host.active],
39
- );
40
- };
41
-
42
- /**
43
- * `<cosmoz-bottom-bar>` is a horizontal responsive bottom toolbar containing items that
44
- * can be used for actions.
45
- *
46
- * The items placed inside the `cosmoz-bottom-bar` are distributed into the toolbar in a horizontal container.
47
- * If the items do not fit the available width, those that do not fit are placed in a dropdown
48
- * menu triggered by a button in the toolbar.
49
- * The class specified by the property `toolbarClass` (default `cosmoz-bottom-bar-toolbar`)
50
- * is applied to items distributed to the toolbar.
51
- * The class specified in the property `menuClass` (default `cosmoz-bottom-bar-menu`)
52
- * is applied to items distributed to the menu.
53
- *
54
- * ### Usage
55
- *
56
- * See demo for example usage
57
- *
58
- * @element cosmoz-bottom-bar
59
- * @demo demo/bottom-bar.html Basic Demo
60
- * @demo demo/bottom-bar-match-parent.html match parent Demo
61
- */
62
- class CosmozBottomBar extends hauntedPolymer(useBottomBar)(PolymerElement) {
63
- static get template() {
64
- return template;
65
- }
66
-
67
- static get properties() {
68
- return {
69
- /**
70
- * Whether the bar is active (shown)
71
- */
72
- active: {
73
- type: Boolean,
74
- value: false,
75
- notify: true,
76
- reflectToAttribute: true,
77
- },
78
-
79
- hideActions: {
80
- type: Boolean,
81
- value: false,
82
- reflectToAttribute: true,
83
- },
84
-
85
- /**
86
- * Bar height (not used when `matchParent` or `matchElementHeight` is set)
87
- */
88
- barHeight: {
89
- type: Number,
90
- value: 64,
91
- },
92
-
93
- /**
94
- * Whether to match the height of parent
95
- */
96
- matchParent: {
97
- type: Boolean,
98
- value: false,
99
- },
100
-
101
- /**
102
- * Whether this bottom bar has items distributed to the menu
103
- */
104
- hasMenuItems: {
105
- type: Boolean,
106
- value: false,
107
- readOnly: true,
108
- notify: true,
109
- },
110
-
111
- hasExtraItems: {
112
- type: Boolean,
113
- value: false,
114
- },
115
-
116
- /**
117
- * Class applied the the selected item
118
- */
119
- selectedClass: {
120
- type: String,
121
- value: 'cosmoz-bottom-bar-selected-item',
122
- },
123
-
124
- /**
125
- * Class applied to items distributed to the toolbar
126
- */
127
- toolbarClass: {
128
- type: String,
129
- value: 'cosmoz-bottom-bar-toolbar',
130
- },
131
-
132
- /**
133
- * Class applied to items distributed to the menu
134
- */
135
- menuClass: {
136
- type: String,
137
- value: 'cosmoz-bottom-bar-menu',
138
- },
139
-
140
- /**
141
- * Maximum number of items in toolbar, regardless of space
142
- */
143
- maxToolbarItems: {
144
- type: Number,
145
- value: 1,
146
- },
147
-
148
- /**
149
- * The actual bar height, depending on if we `matchParent` or set `barHeight`
150
- */
151
- computedBarHeight: {
152
- type: Number,
153
- notify: true,
154
- },
155
- forceOpen: {
156
- type: Boolean,
157
- value: false,
158
- },
159
-
160
- /**
161
- * Whether the bar is visible (has actions and is `active`)
162
- */
163
- visible: {
164
- type: Boolean,
165
- notify: true,
166
- readOnly: true,
167
- computed:
168
- '_computeVisible(hasActions, active, hasExtraItems, forceOpen)',
169
- },
170
-
171
- /**
172
- * Whether we have any visible actions
173
- */
174
- hasActions: {
175
- type: Boolean,
176
- value: false,
177
- readOnly: true,
178
- notify: true,
179
- },
180
-
181
- /**
182
- * Reference element from which to inherit height
183
- */
184
- _matchHeightElement: {
185
- type: Object,
186
- computed: '_getHeightMatchingElement(matchParent)',
187
- },
188
-
189
- topPlacement: {
190
- value: 'top-end',
191
- type: String,
192
- },
193
- };
194
- }
195
-
196
- static get observers() {
197
- return ['_showHideBottomBar(visible)'];
198
- }
199
-
200
- constructor() {
201
- super();
202
- this._boundChildrenUpdated = this._childrenUpdated.bind(this);
203
- this._boundLayoutActions = this._layoutActions.bind(this);
204
- this._resizeObserver = new ResizeObserver((...args) => {
205
- cancelAnimationFrame(this._resizeId);
206
- this._resizeId = requestAnimationFrame(() => this._onResize(...args));
207
- });
208
- }
209
-
210
- connectedCallback() {
211
- super.connectedCallback();
212
-
213
- const layoutOnRemove = (info) =>
214
- info.removedNodes.filter(this._isActionNode) &&
215
- this._debounceLayoutActions();
216
- this._nodeObservers = [
217
- new FlattenedNodesObserver(this.$.content, this._boundChildrenUpdated),
218
- new FlattenedNodesObserver(this.$.extraSlot, (info) =>
219
- this.set('hasExtraItems', info.addedNodes.length > 0),
220
- ),
221
- new FlattenedNodesObserver(this.$.bottomBarToolbar, layoutOnRemove),
222
- new FlattenedNodesObserver(this.$.bottomBarMenu, layoutOnRemove),
223
- ];
224
- this._hiddenMutationObserver = new MutationObserver(() =>
225
- this._debounceLayoutActions(),
226
- );
227
- this._resizeObserver.observe(this);
228
- this.computedBarHeight = this._computeComputedBarHeight(
229
- this._matchHeightElement,
230
- this.barHeight,
231
- );
232
- }
233
-
234
- disconnectedCallback() {
235
- super.disconnectedCallback();
236
- this[rendered] = false;
237
-
238
- [...this._nodeObservers, this._hiddenMutationObserver].forEach((e) =>
239
- e.disconnect(e),
240
- );
241
- this._resizeObserver.unobserve(this);
242
- }
243
-
244
- _childrenUpdated(info) {
245
- const addedNodes = info.addedNodes.filter(this._isActionNode),
246
- removedNodes = info.removedNodes.filter(this._isActionNode),
247
- newNodes = addedNodes.filter((node) => !removedNodes.includes(node));
248
-
249
- if (
250
- (addedNodes.length === 0 && removedNodes.length === 0) ||
251
- newNodes.length === 0
252
- ) {
253
- return;
254
- }
255
- newNodes.forEach((node) => {
256
- this._hiddenMutationObserver.observe(node, {
257
- attributes: true,
258
- attributeFilter: ['hidden'],
259
- });
260
- this._moveElement(node, false);
261
- });
262
-
263
- this._debounceLayoutActions();
264
- }
265
-
266
- _computeComputedBarHeight(matchElementHeight, barHeight) {
267
- if (matchElementHeight) {
268
- return matchElementHeight.offsetHeight;
269
- }
270
- return barHeight;
271
- }
272
-
273
- _computeVisible(hasActions, active, hasExtraItems, forceOpen) {
274
- return forceOpen || ((hasActions || hasExtraItems) && active);
275
- }
276
-
277
- _debounceLayoutActions() {
278
- this._layoutDebouncer = Debouncer.debounce(
279
- this._layoutDebouncer,
280
- timeOut.after(30),
281
- this._boundLayoutActions,
282
- );
283
- }
284
-
285
- _getHeightMatchingElement(matchParent) {
286
- if (matchParent) {
287
- return this.parentElement;
288
- }
289
-
290
- return null;
291
- }
292
-
293
- _getHeightStyle(height) {
294
- return 'height: ' + height + 'px;';
295
- }
296
-
297
- _isActionNode(node) {
298
- return (
299
- node.nodeType === Node.ELEMENT_NODE &&
300
- node.getAttribute('slot') !== 'info' &&
301
- node.tagName !== 'TEMPLATE' &&
302
- node.tagName !== 'STYLE' &&
303
- node.tagName !== 'DOM-REPEAT' &&
304
- node.tagName !== 'DOM-IF' &&
305
- node.getAttribute('slot') !== 'extra'
306
- );
307
- }
308
-
309
- _getElements() {
310
- const elements = FlattenedNodesObserver.getFlattenedNodes(this)
311
- .filter(this._isActionNode)
312
- .filter((element) => !element.hidden)
313
- .sort((a, b) => (a.dataset.index ?? 0) - (b.dataset.index ?? 0));
314
-
315
- if (elements.length === 0) {
316
- return elements;
317
- }
318
-
319
- const topPriorityAction = elements.reduce(
320
- (top, element) => {
321
- return parseInt(top.dataset.priority ?? 0, 10) >=
322
- parseInt(element.dataset.priority ?? 0, 10)
323
- ? top
324
- : element;
325
- },
326
- { dataset: { priority: '-1000' } },
327
- [],
328
- );
329
-
330
- return [
331
- topPriorityAction,
332
- ...elements.filter((e) => e !== topPriorityAction),
333
- ];
334
- }
335
-
336
- /**
337
- * Layout the actions available as buttons or menu items
338
- *
339
- * If the window is resizing down, just make sure that all buttons fits, and if not,
340
- * move one to menu and call itself async (to allow re-rendering) and see if we fit.
341
- * Repeat until the button fits or no buttons are left.
342
- *
343
- * If the window is sizing up, try to place a menu item out as a button, call itself
344
- * async (to allow re-rendering) and see if we fit - if we don't, remove the button again.
345
- *
346
- * We also need to keep track of `_scalingUp` between calls since the resize might fire
347
- * a lot of events, and we don't want to be starting multiple "calculation processes"
348
- * since this will result in an infinite loop.
349
- *
350
- * The actual layouting of actions will be performed by adding or removing the 'button'
351
- * attribute from the action, which will cause it to match different content insertion
352
- * points.
353
- *
354
- * @returns {void}
355
- */
356
- _layoutActions() {
357
- const elements = this._getElements(),
358
- hasActions = elements.length > 0 || this.hasExtraItems;
359
- this._setHasActions(hasActions);
360
-
361
- if (!hasActions) {
362
- // No need to render if we don't have any actions
363
- return this._setHasMenuItems(false);
364
- }
365
-
366
- const toolbarElements = elements.slice(0, this.maxToolbarItems),
367
- menuElements = elements.slice(toolbarElements.length);
368
- toolbarElements.forEach((el) => this._moveElement(el, true));
369
- menuElements.forEach((el) => this._moveElement(el));
370
- this._setHasMenuItems(menuElements.length > 0);
371
- }
372
-
373
- _moveElement(element, toToolbar) {
374
- const slot = toToolbar ? BOTTOM_BAR_TOOLBAR_SLOT : BOTTOM_BAR_MENU_SLOT,
375
- tabindex = '0';
376
- element.setAttribute('slot', slot);
377
- element.setAttribute('tabindex', tabindex);
378
- element.classList.toggle(this.menuClass, !toToolbar);
379
- element.classList.toggle(this.toolbarClass, toToolbar);
380
- this.updateStyles();
381
- }
382
-
383
- _onResize([entry]) {
384
- const hidden =
385
- entry.borderBoxSize?.[0]?.inlineSize === 0 ||
386
- entry.contentRect?.width === 0;
387
- if (hidden) {
388
- return;
389
- }
390
- this.computedBarHeight = this._computeComputedBarHeight(
391
- this._matchHeightElement,
392
- this.barHeight,
393
- );
394
- }
395
-
396
- _showHideBottomBar(visible) {
397
- this.style.transitionDuration = 0;
398
- this.style.display = '';
399
- this.style.maxHeight = '';
400
-
401
- const height = this.computedBarHeight,
402
- to = !visible ? '0px' : height + 'px';
403
-
404
- let from = visible ? '0px' : height + 'px';
405
-
406
- if (!this[rendered]) {
407
- from = to;
408
- this[rendered] = true;
409
- }
410
-
411
- this.style.maxHeight = from;
412
-
413
- const onEnd = () => {
414
- this.removeEventListener('transitionend', onEnd);
415
- this.style.maxHeight = '';
416
- this.style.display = this.visible ? '' : 'none';
417
- };
418
- requestAnimationFrame(() => {
419
- this.addEventListener('transitionend', onEnd);
420
- this.style.transitionDuration = '';
421
- this.style.maxHeight = to;
422
- });
423
- }
424
- }
425
-
426
- customElements.define('cosmoz-bottom-bar', CosmozBottomBar);
427
-
428
- const tmplt = `
429
- <slot name="extra" slot="extra"></slot>
430
- <slot name="bottom-bar-toolbar" slot="bottom-bar-toolbar"></slot>
431
- <slot name="bottom-bar-menu" slot="bottom-bar-menu"></slot>
432
- `;
433
-
434
- export const bottomBarSlots = html(Object.assign([tmplt], { raw: [tmplt] })),
435
- bottomBarSlotsPolymer = polymerHtml(Object.assign([tmplt], { raw: [tmplt] }));
@@ -1,232 +0,0 @@
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 { useActivity } from '@neovici/cosmoz-utils/keybindings/use-activity';
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
- export const openMenu = Symbol('openMenu');
124
-
125
- const openActionsMenu = (host) => {
126
- const dropdown = host.shadowRoot.querySelector('#dropdown');
127
-
128
- if (dropdown.hasAttribute('hidden')) return;
129
-
130
- //TODO: Clean up when open function is implemented for cosmoz-dropdown-menu
131
- dropdown.shadowRoot
132
- .querySelector('cosmoz-dropdown')
133
- .shadowRoot.getElementById('dropdownButton')
134
- .click();
135
- };
136
-
137
- /**
138
- * `<cosmoz-bottom-bar>` is a horizontal responsive bottom toolbar containing items that
139
- * can be used for actions.
140
- *
141
- * The items placed inside the `cosmoz-bottom-bar` are distributed into the toolbar in a horizontal container.
142
- * If the items do not fit the available width, those that do not fit are placed in a dropdown
143
- * menu triggered by a button in the toolbar.
144
- * The class specified by the property `toolbarClass` (default `cosmoz-bottom-bar-toolbar`)
145
- * is applied to items distributed to the toolbar.
146
- * The class specified in the property `menuClass` (default `cosmoz-bottom-bar-menu`)
147
- * is applied to items distributed to the menu.
148
- *
149
- * ### Usage
150
- *
151
- * See demo for example usage
152
- *
153
- * @element cosmoz-bottom-bar
154
- * @demo demo/bottom-bar-next.html Basic Demo
155
- */
156
- // eslint-disable-next-line max-statements
157
- const CosmozBottomBar = ({ active = false, maxToolbarItems = 1 }) => {
158
- const host = useHost();
159
-
160
- useActivity(
161
- {
162
- activity: openMenu,
163
- callback: () => openActionsMenu(host),
164
- check: () => host.active && !host.hasAttribute('hide-actions'),
165
- element: () => host.shadowRoot.querySelector('#dropdown'),
166
- },
167
- [host.active],
168
- );
169
-
170
- const toggle = toggleSize('height');
171
-
172
- useLayoutEffect(() => {
173
- toggle(host, active);
174
- }, [active]);
175
-
176
- const slotChangeHandler = () => {
177
- _layoutActions(host, maxToolbarItems);
178
- };
179
-
180
- return html`<div id="bar" part="bar">
181
- <div id="info" part="info"><slot name="info"></slot></div>
182
- <slot
183
- id="bottomBarToolbar"
184
- name="bottom-bar-toolbar"
185
- @slotchange=${slotChangeHandler}
186
- ></slot>
187
- <cosmoz-dropdown-menu id="dropdown">
188
- <svg
189
- slot="button"
190
- width="4"
191
- height="16"
192
- viewBox="0 0 4 16"
193
- fill="none"
194
- xmlns="http://www.w3.org/2000/svg"
195
- >
196
- <path
197
- fill-rule="evenodd"
198
- clip-rule="evenodd"
199
- 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"
200
- fill="white"
201
- />
202
- <path
203
- fill-rule="evenodd"
204
- clip-rule="evenodd"
205
- 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"
206
- fill="white"
207
- />
208
- <path
209
- fill-rule="evenodd"
210
- clip-rule="evenodd"
211
- 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"
212
- fill="white"
213
- />
214
- </svg>
215
- <slot id="bottomBarMenu" name="bottom-bar-menu"></slot>
216
- </cosmoz-dropdown-menu>
217
- <slot name="extra" id="extraSlot"></slot>
218
- </div>
219
- <div hidden style="display:none">
220
- <slot id="content" @slotchange=${slotChangeHandler}></slot>
221
- </div>`;
222
- };
223
-
224
- export default CosmozBottomBar;
225
-
226
- customElements.define(
227
- 'cosmoz-bottom-bar-next',
228
- component(CosmozBottomBar, {
229
- observedAttributes: ['active'],
230
- styleSheets: [style],
231
- }),
232
- );
@@ -1,98 +0,0 @@
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
- z-index: 1;
21
-
22
- --cosmoz-dropdown-anchor-spacing: 12px 6px;
23
- --paper-button: {
24
- background-color: inherit;
25
- text-transform: none;
26
- border: 0;
27
- border-radius: 0;
28
- font-size: inherit;
29
- color: inherit;
30
- font-weight: inherit;
31
- margin: 0;
32
- padding: 0;
33
- };
34
- }
35
- :host([force-open]) {
36
- transition: none;
37
- }
38
- [hidden],
39
- ::slotted([hidden]) {
40
- display: none !important;
41
- }
42
- #bar {
43
- height: 64px;
44
- padding: 0 3%;
45
- display: flex;
46
- align-items: center;
47
- }
48
- #info {
49
- min-width: 5px;
50
- padding-right: 3%;
51
- margin-right: auto;
52
- white-space: nowrap;
53
- }
54
- #bottomBarToolbar::slotted(:not(slot):not([unstyled])) {
55
- margin: 0 0.29em;
56
- min-width: 40px;
57
- min-height: 40px;
58
- text-overflow: ellipsis;
59
- white-space: nowrap;
60
- background: var(
61
- --cosmoz-bottom-bar-button-bg-color,
62
- var(--cosmoz-button-bg-color, #101010)
63
- );
64
- color: var(
65
- --cosmoz-bottom-bar-button-color,
66
- var(--cosmoz-button-color, #fff)
67
- );
68
- border-radius: 6px;
69
- padding: 0 18px;
70
- font-size: 14px;
71
- font-weight: 500;
72
- line-height: 40px;
73
- }
74
-
75
- #bottomBarToolbar::slotted(:not(slot)[disabled]) {
76
- opacity: var(--cosmoz-button-disabled-opacity, 0.15);
77
- pointer-events: none;
78
- }
79
- #bottomBarToolbar::slotted(:not(slot):hover) {
80
- background: var(
81
- --cosmoz-bottom-bar-button-hover-bg-color,
82
- var(--cosmoz-button-hover-bg-color, #3a3f44)
83
- );
84
- }
85
- #dropdown::part(content) {
86
- max-width: 300px;
87
- }
88
-
89
- :host([hide-actions]) #bottomBarToolbar,
90
- :host([hide-actions]) #bottomBarMenu,
91
- :host([hide-actions]) #dropdown {
92
- display: none;
93
- }
94
-
95
- :host(:not([has-menu-items])) cosmoz-dropdown-menu {
96
- display: none;
97
- }
98
- `;