@record-evolution/widget-navbar 1.0.14 → 1.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/widget-navbar.js +121 -71
- package/dist/widget-navbar.js.map +1 -1
- package/package.json +2 -2
- package/src/definition-schema.d.ts +1 -1
- package/src/definition-schema.json +16 -2
- package/src/widget-navbar.ts +95 -30
package/dist/widget-navbar.js
CHANGED
|
@@ -1,18 +1,22 @@
|
|
|
1
|
-
import { LitElement as
|
|
2
|
-
import { property as
|
|
3
|
-
import { repeat as
|
|
1
|
+
import { LitElement as f, css as u, nothing as p, html as c } from "lit";
|
|
2
|
+
import { property as d, state as h } from "lit/decorators.js";
|
|
3
|
+
import { repeat as m } from "lit/directives/repeat.js";
|
|
4
4
|
import "@material/web/icon/icon";
|
|
5
|
-
import "@material/web/tabs/tabs";
|
|
6
|
-
import "@material/web/tabs/primary-tab";
|
|
7
5
|
import "@material/web/tabs/secondary-tab";
|
|
8
|
-
var b = Object.defineProperty,
|
|
9
|
-
for (var
|
|
10
|
-
(
|
|
11
|
-
return
|
|
6
|
+
var b = Object.defineProperty, l = (s, t, e, a) => {
|
|
7
|
+
for (var o = void 0, r = s.length - 1, n; r >= 0; r--)
|
|
8
|
+
(n = s[r]) && (o = n(t, e, o) || o);
|
|
9
|
+
return o && b(t, e, o), o;
|
|
12
10
|
};
|
|
13
|
-
const
|
|
11
|
+
const g = (s) => {
|
|
12
|
+
if (!s) return !0;
|
|
13
|
+
const t = s.trim().toLowerCase();
|
|
14
|
+
if (t === "transparent") return !0;
|
|
15
|
+
const e = t.match(/^(?:rgba|hsla)\([^)]*[,/]\s*([\d.]+%?)\s*\)$/);
|
|
16
|
+
return e ? parseFloat(e[1]) === 0 : /^#[0-9a-f]{8}$/.test(t) ? t.slice(7) === "00" : /^#[0-9a-f]{4}$/.test(t) ? t[4] === "0" : !1;
|
|
17
|
+
}, v = class v extends f {
|
|
14
18
|
constructor() {
|
|
15
|
-
super(...arguments), this.version = "1.0.
|
|
19
|
+
super(...arguments), this.version = "1.0.16";
|
|
16
20
|
}
|
|
17
21
|
update(t) {
|
|
18
22
|
t.has("theme") && this.registerTheme(this.theme), super.update(t);
|
|
@@ -21,8 +25,8 @@ const h = class h extends m {
|
|
|
21
25
|
this.registerTheme(this.theme);
|
|
22
26
|
}
|
|
23
27
|
registerTheme(t) {
|
|
24
|
-
const e = getComputedStyle(this).getPropertyValue("--re-text-color").trim(),
|
|
25
|
-
this.themeBgColor =
|
|
28
|
+
const e = getComputedStyle(this).getPropertyValue("--re-text-color").trim(), a = getComputedStyle(this).getPropertyValue("--re-tile-background-color").trim(), o = t?.theme_object?.backgroundColor;
|
|
29
|
+
this.themeBgColor = a || (g(o) ? void 0 : o), this.themeTitleColor = e || t?.theme_object?.title?.textStyle?.color;
|
|
26
30
|
}
|
|
27
31
|
handleNavItemClick(t) {
|
|
28
32
|
const e = new CustomEvent("nav-submit", {
|
|
@@ -41,29 +45,29 @@ const h = class h extends m {
|
|
|
41
45
|
resolveRoute(t) {
|
|
42
46
|
let e = String(t?.route ?? "");
|
|
43
47
|
if (t?.variables)
|
|
44
|
-
for (const
|
|
45
|
-
|
|
48
|
+
for (const a of t.variables)
|
|
49
|
+
a.label && (e = e.split(`{{${a.label}}}`).join(encodeURIComponent(String(a.value ?? ""))));
|
|
46
50
|
if (e.includes("*")) {
|
|
47
|
-
const
|
|
48
|
-
for (let
|
|
49
|
-
r
|
|
50
|
-
e = (e.startsWith("/") ? "/" : "") +
|
|
51
|
+
const a = (this.route || "").split("/").filter(Boolean), o = e.split("/").filter(Boolean);
|
|
52
|
+
for (let r = 0; r < o.length; r++)
|
|
53
|
+
o[r] === "*" && (o[r] = a[r] ?? "");
|
|
54
|
+
e = (e.startsWith("/") ? "/" : "") + o.filter(Boolean).join("/");
|
|
51
55
|
}
|
|
52
56
|
return e;
|
|
53
57
|
}
|
|
54
58
|
matchesRoute(t) {
|
|
55
59
|
if (t === void 0) return !1;
|
|
56
|
-
const e = this.trimRoute(decodeURIComponent(this.route || "/")),
|
|
57
|
-
return t.startsWith("/") ? e.startsWith(
|
|
60
|
+
const e = this.trimRoute(decodeURIComponent(this.route || "/")), a = this.trimRoute(t);
|
|
61
|
+
return t.startsWith("/") ? e.startsWith(a) : e.endsWith(a) || e === a;
|
|
58
62
|
}
|
|
59
63
|
render() {
|
|
60
|
-
const t = this.inputData?.style?.fontSize ?? 16, e =
|
|
61
|
-
return
|
|
64
|
+
const t = this.inputData?.style?.fontSize ?? 16, e = this.inputData?.style?.fontWeight ?? 400, a = t * 1.5, o = this.inputData?.style?.color || this.themeTitleColor || "black", r = this.inputData?.style?.backgroundColor || this.themeBgColor || "white";
|
|
65
|
+
return c`
|
|
62
66
|
<div
|
|
63
67
|
class="wrapper"
|
|
64
|
-
style="
|
|
65
|
-
font-weight: ${
|
|
66
|
-
|
|
68
|
+
style="--nav-bg: ${r}; --nav-fg: ${o};
|
|
69
|
+
--nav-font-size: ${t}px; --nav-font-weight: ${e};
|
|
70
|
+
--nav-icon-size: ${a}px;"
|
|
67
71
|
>
|
|
68
72
|
<h2
|
|
69
73
|
class="paging"
|
|
@@ -78,36 +82,26 @@ const h = class h extends m {
|
|
|
78
82
|
>
|
|
79
83
|
${this.inputData?.title}
|
|
80
84
|
</h2>
|
|
81
|
-
<
|
|
82
|
-
${
|
|
85
|
+
<div class="nav-list" role="tablist">
|
|
86
|
+
${m(
|
|
83
87
|
this.inputData?.navItems || [],
|
|
84
|
-
(
|
|
85
|
-
(
|
|
88
|
+
(n) => n.label,
|
|
89
|
+
(n) => c`
|
|
86
90
|
<md-secondary-tab
|
|
87
|
-
?active=${this.matchesRoute(this.resolveRoute(
|
|
88
|
-
|
|
89
|
-
--md-secondary-tab-container-color: ${r};
|
|
90
|
-
color: ${i};"
|
|
91
|
-
@click=${() => this.handleNavItemClick(this.resolveRoute(o))}
|
|
91
|
+
?active=${this.matchesRoute(this.resolveRoute(n))}
|
|
92
|
+
@click=${() => this.handleNavItemClick(this.resolveRoute(n))}
|
|
92
93
|
>
|
|
93
|
-
${
|
|
94
|
-
|
|
95
|
-
slot="icon"
|
|
96
|
-
style="font-size: ${e}px;width: ${e}px;color: ${i}"
|
|
97
|
-
>
|
|
98
|
-
${o.iconName}
|
|
99
|
-
</md-icon>
|
|
100
|
-
` : f}
|
|
101
|
-
${o.label}
|
|
94
|
+
${n.iconName ? c`<md-icon slot="icon">${n.iconName}</md-icon>` : p}
|
|
95
|
+
${n.label}
|
|
102
96
|
</md-secondary-tab>
|
|
103
97
|
`
|
|
104
98
|
)}
|
|
105
|
-
</
|
|
99
|
+
</div>
|
|
106
100
|
</div>
|
|
107
101
|
`;
|
|
108
102
|
}
|
|
109
103
|
};
|
|
110
|
-
|
|
104
|
+
v.styles = u`
|
|
111
105
|
:host {
|
|
112
106
|
display: block;
|
|
113
107
|
font-family: sans-serif;
|
|
@@ -122,47 +116,103 @@ h.styles = d`
|
|
|
122
116
|
.wrapper {
|
|
123
117
|
display: flex;
|
|
124
118
|
align-items: end;
|
|
119
|
+
gap: 8px;
|
|
125
120
|
height: 100%;
|
|
126
121
|
width: 100%;
|
|
127
122
|
padding: 0 16px;
|
|
128
123
|
box-sizing: border-box;
|
|
124
|
+
background-color: var(--nav-bg);
|
|
125
|
+
color: var(--nav-fg);
|
|
126
|
+
font-size: var(--nav-font-size);
|
|
127
|
+
font-weight: var(--nav-font-weight);
|
|
129
128
|
}
|
|
130
129
|
|
|
131
|
-
|
|
132
|
-
|
|
130
|
+
/* One scrollable row. Without this the tabs wrap onto several lines and
|
|
131
|
+
spill outside the coloured bar as soon as they don't fit. */
|
|
132
|
+
.nav-list {
|
|
133
|
+
display: flex;
|
|
134
|
+
align-items: end;
|
|
135
|
+
flex: 1;
|
|
136
|
+
min-width: 0;
|
|
137
|
+
overflow-x: auto;
|
|
138
|
+
scrollbar-width: none;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.nav-list::-webkit-scrollbar {
|
|
142
|
+
display: none;
|
|
133
143
|
}
|
|
134
144
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
145
|
+
/* Material's tab tokens default to the M3 palette (purple indicator,
|
|
146
|
+
near-black labels), which ignores the widget's configured colours.
|
|
147
|
+
Map every colour token onto the nav colours. */
|
|
148
|
+
md-secondary-tab {
|
|
149
|
+
flex: none;
|
|
150
|
+
--md-secondary-tab-container-color: var(--nav-bg);
|
|
151
|
+
--md-secondary-tab-label-text-size: var(--nav-font-size);
|
|
152
|
+
--md-secondary-tab-label-text-weight: var(--nav-font-weight);
|
|
153
|
+
--md-secondary-tab-icon-size: var(--nav-icon-size);
|
|
154
|
+
--md-secondary-tab-label-text-color: var(--nav-fg);
|
|
155
|
+
--md-secondary-tab-hover-label-text-color: var(--nav-fg);
|
|
156
|
+
--md-secondary-tab-focus-label-text-color: var(--nav-fg);
|
|
157
|
+
--md-secondary-tab-pressed-label-text-color: var(--nav-fg);
|
|
158
|
+
--md-secondary-tab-active-label-text-color: var(--nav-fg);
|
|
159
|
+
--md-secondary-tab-active-hover-label-text-color: var(--nav-fg);
|
|
160
|
+
--md-secondary-tab-active-focus-label-text-color: var(--nav-fg);
|
|
161
|
+
--md-secondary-tab-active-pressed-label-text-color: var(--nav-fg);
|
|
162
|
+
--md-secondary-tab-icon-color: var(--nav-fg);
|
|
163
|
+
--md-secondary-tab-hover-icon-color: var(--nav-fg);
|
|
164
|
+
--md-secondary-tab-focus-icon-color: var(--nav-fg);
|
|
165
|
+
--md-secondary-tab-pressed-icon-color: var(--nav-fg);
|
|
166
|
+
--md-secondary-tab-active-icon-color: var(--nav-fg);
|
|
167
|
+
--md-secondary-tab-active-hover-icon-color: var(--nav-fg);
|
|
168
|
+
--md-secondary-tab-active-focus-icon-color: var(--nav-fg);
|
|
169
|
+
--md-secondary-tab-active-pressed-icon-color: var(--nav-fg);
|
|
170
|
+
--md-secondary-tab-active-indicator-color: var(--nav-fg);
|
|
171
|
+
--md-secondary-tab-hover-state-layer-color: var(--nav-fg);
|
|
172
|
+
--md-secondary-tab-pressed-state-layer-color: var(--nav-fg);
|
|
173
|
+
--md-secondary-tab-active-hover-state-layer-color: var(--nav-fg);
|
|
174
|
+
--md-secondary-tab-active-pressed-state-layer-color: var(--nav-fg);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
md-icon {
|
|
178
|
+
font-family: 'Material Symbols Outlined';
|
|
179
|
+
font-size: var(--nav-icon-size);
|
|
180
|
+
width: var(--nav-icon-size);
|
|
181
|
+
height: var(--nav-icon-size);
|
|
182
|
+
color: var(--nav-fg);
|
|
138
183
|
}
|
|
139
184
|
|
|
140
185
|
h2 {
|
|
141
186
|
margin: 0;
|
|
142
187
|
padding: 0px;
|
|
188
|
+
flex: none;
|
|
189
|
+
display: flex;
|
|
190
|
+
align-items: center;
|
|
191
|
+
min-height: 48px;
|
|
143
192
|
font-size: 1.2em;
|
|
144
|
-
|
|
145
|
-
|
|
193
|
+
/* the UA stylesheet forces bold, which would ignore the
|
|
194
|
+
configured font weight */
|
|
195
|
+
font-weight: inherit;
|
|
146
196
|
}
|
|
147
197
|
`;
|
|
148
|
-
let
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
],
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
],
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
],
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
],
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
],
|
|
164
|
-
window.customElements.define("widget-navbar-1.0.
|
|
198
|
+
let i = v;
|
|
199
|
+
l([
|
|
200
|
+
d({ type: Object })
|
|
201
|
+
], i.prototype, "inputData");
|
|
202
|
+
l([
|
|
203
|
+
d({ type: Object })
|
|
204
|
+
], i.prototype, "theme");
|
|
205
|
+
l([
|
|
206
|
+
d({ type: String })
|
|
207
|
+
], i.prototype, "route");
|
|
208
|
+
l([
|
|
209
|
+
h()
|
|
210
|
+
], i.prototype, "themeBgColor");
|
|
211
|
+
l([
|
|
212
|
+
h()
|
|
213
|
+
], i.prototype, "themeTitleColor");
|
|
214
|
+
window.customElements.define("widget-navbar-1.0.16", i);
|
|
165
215
|
export {
|
|
166
|
-
|
|
216
|
+
i as WidgetNavbar
|
|
167
217
|
};
|
|
168
218
|
//# sourceMappingURL=widget-navbar.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"widget-navbar.js","sources":["../src/widget-navbar.ts"],"sourcesContent":["import { html, css, LitElement, PropertyValues, nothing } from 'lit'\nimport { property, state } from 'lit/decorators.js'\nimport { repeat } from 'lit/directives/repeat.js'\nimport { InputData } from './definition-schema'\n\nimport '@material/web/icon/icon'\nimport '@material/web/tabs/tabs'\nimport '@material/web/tabs/primary-tab'\nimport '@material/web/tabs/secondary-tab'\n\ntype Theme = {\n theme_name: string\n theme_object: any\n}\nexport class WidgetNavbar extends LitElement {\n @property({ type: Object }) inputData?: InputData\n @property({ type: Object }) theme?: Theme\n @property({ type: String }) route?: string\n\n @state() private themeBgColor?: string\n @state() private themeTitleColor?: string\n\n version: string = 'versionplaceholder'\n\n update(changedProperties: Map<string, any>) {\n if (changedProperties.has('theme')) {\n this.registerTheme(this.theme)\n }\n\n super.update(changedProperties)\n }\n\n protected firstUpdated(_changedProperties: PropertyValues): void {\n this.registerTheme(this.theme)\n }\n\n registerTheme(theme?: Theme) {\n const cssTextColor = getComputedStyle(this).getPropertyValue('--re-text-color').trim()\n const cssBgColor = getComputedStyle(this).getPropertyValue('--re-tile-background-color').trim()\n this.themeBgColor = cssBgColor || this.theme?.theme_object?.backgroundColor\n this.themeTitleColor = cssTextColor || this.theme?.theme_object?.title?.textStyle?.color\n }\n\n handleNavItemClick(route?: string) {\n // console.log('Navigating to:', item.route)\n const event = new CustomEvent('nav-submit', {\n detail: { path: route },\n bubbles: true,\n composed: true\n })\n this.dispatchEvent(event)\n }\n\n normalizeRoute(route?: string) {\n if (!route) return '/'\n return route\n }\n\n static styles = css`\n :host {\n display: block;\n font-family: sans-serif;\n box-sizing: border-box;\n margin: auto;\n }\n\n .paging:not([active]) {\n display: none !important;\n }\n\n .wrapper {\n display: flex;\n align-items: end;\n height: 100%;\n width: 100%;\n padding: 0 16px;\n box-sizing: border-box;\n }\n\n md-icon {\n font-family: 'Material Symbols Outlined';\n }\n\n .selected {\n font-weight: bold;\n background-color: rgba(0, 0, 0, 0.1);\n }\n\n h2 {\n margin: 0;\n padding: 0px;\n font-size: 1.2em;\n line-height: 2.5;\n height: 48px;\n }\n `\n\n trimRoute(route?: string) {\n if (!route) return ''\n return '/' + route.split('/').filter(Boolean).join('/')\n }\n\n resolveRoute(item?: any): string | undefined {\n let route = String(item?.route ?? '')\n if (item?.variables) {\n for (const variable of item.variables) {\n if (variable.label) {\n route = route\n .split(`{{${variable.label}}}`)\n .join(encodeURIComponent(String(variable.value ?? '')))\n }\n }\n }\n if (route.includes('*')) {\n const currentSegments = (this.route || '').split('/').filter(Boolean)\n const routeSegments = route.split('/').filter(Boolean)\n for (let i = 0; i < routeSegments.length; i++) {\n if (routeSegments[i] === '*') {\n routeSegments[i] = currentSegments[i] ?? ''\n }\n }\n route = (route.startsWith('/') ? '/' : '') + routeSegments.filter(Boolean).join('/')\n }\n return route\n }\n\n matchesRoute(itemRoute?: string) {\n if (itemRoute === undefined) return false\n const route = this.trimRoute(decodeURIComponent(this.route || '/'))\n const subRoute = this.trimRoute(itemRoute)\n if (itemRoute.startsWith('/')) {\n return route.startsWith(subRoute)\n } else {\n return route.endsWith(subRoute) || route === subRoute\n }\n }\n\n render() {\n const fontSize = this.inputData?.style?.fontSize ?? 16\n const iconFontSize = (this.inputData?.style?.fontSize ?? 16) * 1.5\n const fontColor = this.inputData?.style?.color || this.themeTitleColor || 'black'\n const bgColor = this.inputData?.style?.backgroundColor || this.themeBgColor || 'white'\n return html`\n <div\n class=\"wrapper\"\n style=\"background-color: ${bgColor}; color: ${fontColor};\n font-weight: ${this.inputData?.style?.fontWeight};\n font-size: ${fontSize}px;\"\n >\n <h2\n class=\"paging\"\n style=${this.inputData?.route ? 'cursor: pointer' : ''}\n ?active=${this.inputData?.title}\n @click=${() =>\n this.handleNavItemClick(\n this.resolveRoute({\n route: this.inputData?.route,\n variables: this.inputData?.variables\n })\n )}\n >\n ${this.inputData?.title}\n </h2>\n <md-tab class=\"xnav-list\">\n ${repeat(\n this.inputData?.navItems || [],\n (item) => item.label,\n (item) => html`\n <md-secondary-tab\n ?active=${this.matchesRoute(this.resolveRoute(item))}\n style=\"font-size: ${fontSize}px;\n --md-secondary-tab-container-color: ${bgColor};\n color: ${fontColor};\"\n @click=${() => this.handleNavItemClick(this.resolveRoute(item))}\n >\n ${item.iconName\n ? html`\n <md-icon\n slot=\"icon\"\n style=\"font-size: ${iconFontSize}px;width: ${iconFontSize}px;color: ${fontColor}\"\n >\n ${item.iconName}\n </md-icon>\n `\n : nothing}\n ${item.label}\n </md-secondary-tab>\n `\n )}\n </md-tab>\n </div>\n `\n }\n}\nwindow.customElements.define('widget-navbar-versionplaceholder', WidgetNavbar)\n"],"names":["_WidgetNavbar","LitElement","changedProperties","_changedProperties","theme","cssTextColor","cssBgColor","route","event","item","variable","currentSegments","routeSegments","i","itemRoute","subRoute","fontSize","iconFontSize","fontColor","bgColor","html","repeat","nothing","css","WidgetNavbar","__decorateClass","property","state"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"widget-navbar.js","sources":["../src/widget-navbar.ts"],"sourcesContent":["import { html, css, LitElement, PropertyValues, nothing } from 'lit'\nimport { property, state } from 'lit/decorators.js'\nimport { repeat } from 'lit/directives/repeat.js'\nimport { NavigationBarConfiguration } from './definition-schema'\n\nimport '@material/web/icon/icon'\nimport '@material/web/tabs/secondary-tab'\n\ntype Theme = {\n theme_name: string\n theme_object: any\n}\n\n/**\n * ECharts themes describe a *chart canvas*, and most of them set\n * `backgroundColor` to a fully transparent colour (the light theme uses\n * `rgba(0, 0, 0, 0)`). That is a valid, truthy colour string, so using it\n * verbatim as the navbar background paints nothing at all. Treat any\n * fully transparent theme colour as \"no colour\" so the fallback kicks in.\n */\nconst isTransparent = (color?: string) => {\n if (!color) return true\n const value = color.trim().toLowerCase()\n if (value === 'transparent') return true\n const functional = value.match(/^(?:rgba|hsla)\\([^)]*[,/]\\s*([\\d.]+%?)\\s*\\)$/)\n if (functional) return parseFloat(functional[1]) === 0\n // #rrggbbaa / #rgba\n if (/^#[0-9a-f]{8}$/.test(value)) return value.slice(7) === '00'\n if (/^#[0-9a-f]{4}$/.test(value)) return value[4] === '0'\n return false\n}\nexport class WidgetNavbar extends LitElement {\n @property({ type: Object }) inputData?: NavigationBarConfiguration\n @property({ type: Object }) theme?: Theme\n @property({ type: String }) route?: string\n\n @state() private themeBgColor?: string\n @state() private themeTitleColor?: string\n\n version: string = 'versionplaceholder'\n\n update(changedProperties: Map<string, any>) {\n if (changedProperties.has('theme')) {\n this.registerTheme(this.theme)\n }\n\n super.update(changedProperties)\n }\n\n protected firstUpdated(_changedProperties: PropertyValues): void {\n this.registerTheme(this.theme)\n }\n\n registerTheme(theme?: Theme) {\n const cssTextColor = getComputedStyle(this).getPropertyValue('--re-text-color').trim()\n const cssBgColor = getComputedStyle(this).getPropertyValue('--re-tile-background-color').trim()\n const themeBgColor = theme?.theme_object?.backgroundColor\n this.themeBgColor = cssBgColor || (isTransparent(themeBgColor) ? undefined : themeBgColor)\n this.themeTitleColor = cssTextColor || theme?.theme_object?.title?.textStyle?.color\n }\n\n handleNavItemClick(route?: string) {\n // console.log('Navigating to:', item.route)\n const event = new CustomEvent('nav-submit', {\n detail: { path: route },\n bubbles: true,\n composed: true\n })\n this.dispatchEvent(event)\n }\n\n normalizeRoute(route?: string) {\n if (!route) return '/'\n return route\n }\n\n static styles = css`\n :host {\n display: block;\n font-family: sans-serif;\n box-sizing: border-box;\n margin: auto;\n }\n\n .paging:not([active]) {\n display: none !important;\n }\n\n .wrapper {\n display: flex;\n align-items: end;\n gap: 8px;\n height: 100%;\n width: 100%;\n padding: 0 16px;\n box-sizing: border-box;\n background-color: var(--nav-bg);\n color: var(--nav-fg);\n font-size: var(--nav-font-size);\n font-weight: var(--nav-font-weight);\n }\n\n /* One scrollable row. Without this the tabs wrap onto several lines and\n spill outside the coloured bar as soon as they don't fit. */\n .nav-list {\n display: flex;\n align-items: end;\n flex: 1;\n min-width: 0;\n overflow-x: auto;\n scrollbar-width: none;\n }\n\n .nav-list::-webkit-scrollbar {\n display: none;\n }\n\n /* Material's tab tokens default to the M3 palette (purple indicator,\n near-black labels), which ignores the widget's configured colours.\n Map every colour token onto the nav colours. */\n md-secondary-tab {\n flex: none;\n --md-secondary-tab-container-color: var(--nav-bg);\n --md-secondary-tab-label-text-size: var(--nav-font-size);\n --md-secondary-tab-label-text-weight: var(--nav-font-weight);\n --md-secondary-tab-icon-size: var(--nav-icon-size);\n --md-secondary-tab-label-text-color: var(--nav-fg);\n --md-secondary-tab-hover-label-text-color: var(--nav-fg);\n --md-secondary-tab-focus-label-text-color: var(--nav-fg);\n --md-secondary-tab-pressed-label-text-color: var(--nav-fg);\n --md-secondary-tab-active-label-text-color: var(--nav-fg);\n --md-secondary-tab-active-hover-label-text-color: var(--nav-fg);\n --md-secondary-tab-active-focus-label-text-color: var(--nav-fg);\n --md-secondary-tab-active-pressed-label-text-color: var(--nav-fg);\n --md-secondary-tab-icon-color: var(--nav-fg);\n --md-secondary-tab-hover-icon-color: var(--nav-fg);\n --md-secondary-tab-focus-icon-color: var(--nav-fg);\n --md-secondary-tab-pressed-icon-color: var(--nav-fg);\n --md-secondary-tab-active-icon-color: var(--nav-fg);\n --md-secondary-tab-active-hover-icon-color: var(--nav-fg);\n --md-secondary-tab-active-focus-icon-color: var(--nav-fg);\n --md-secondary-tab-active-pressed-icon-color: var(--nav-fg);\n --md-secondary-tab-active-indicator-color: var(--nav-fg);\n --md-secondary-tab-hover-state-layer-color: var(--nav-fg);\n --md-secondary-tab-pressed-state-layer-color: var(--nav-fg);\n --md-secondary-tab-active-hover-state-layer-color: var(--nav-fg);\n --md-secondary-tab-active-pressed-state-layer-color: var(--nav-fg);\n }\n\n md-icon {\n font-family: 'Material Symbols Outlined';\n font-size: var(--nav-icon-size);\n width: var(--nav-icon-size);\n height: var(--nav-icon-size);\n color: var(--nav-fg);\n }\n\n h2 {\n margin: 0;\n padding: 0px;\n flex: none;\n display: flex;\n align-items: center;\n min-height: 48px;\n font-size: 1.2em;\n /* the UA stylesheet forces bold, which would ignore the\n configured font weight */\n font-weight: inherit;\n }\n `\n\n trimRoute(route?: string) {\n if (!route) return ''\n return '/' + route.split('/').filter(Boolean).join('/')\n }\n\n resolveRoute(item?: any): string | undefined {\n let route = String(item?.route ?? '')\n if (item?.variables) {\n for (const variable of item.variables) {\n if (variable.label) {\n route = route\n .split(`{{${variable.label}}}`)\n .join(encodeURIComponent(String(variable.value ?? '')))\n }\n }\n }\n if (route.includes('*')) {\n const currentSegments = (this.route || '').split('/').filter(Boolean)\n const routeSegments = route.split('/').filter(Boolean)\n for (let i = 0; i < routeSegments.length; i++) {\n if (routeSegments[i] === '*') {\n routeSegments[i] = currentSegments[i] ?? ''\n }\n }\n route = (route.startsWith('/') ? '/' : '') + routeSegments.filter(Boolean).join('/')\n }\n return route\n }\n\n matchesRoute(itemRoute?: string) {\n if (itemRoute === undefined) return false\n const route = this.trimRoute(decodeURIComponent(this.route || '/'))\n const subRoute = this.trimRoute(itemRoute)\n if (itemRoute.startsWith('/')) {\n return route.startsWith(subRoute)\n } else {\n return route.endsWith(subRoute) || route === subRoute\n }\n }\n\n render() {\n const fontSize = this.inputData?.style?.fontSize ?? 16\n const fontWeight = this.inputData?.style?.fontWeight ?? 400\n const iconFontSize = fontSize * 1.5\n const fontColor = this.inputData?.style?.color || this.themeTitleColor || 'black'\n const bgColor = this.inputData?.style?.backgroundColor || this.themeBgColor || 'white'\n return html`\n <div\n class=\"wrapper\"\n style=\"--nav-bg: ${bgColor}; --nav-fg: ${fontColor};\n --nav-font-size: ${fontSize}px; --nav-font-weight: ${fontWeight};\n --nav-icon-size: ${iconFontSize}px;\"\n >\n <h2\n class=\"paging\"\n style=${this.inputData?.route ? 'cursor: pointer' : ''}\n ?active=${this.inputData?.title}\n @click=${() =>\n this.handleNavItemClick(\n this.resolveRoute({\n route: this.inputData?.route,\n variables: this.inputData?.variables\n })\n )}\n >\n ${this.inputData?.title}\n </h2>\n <div class=\"nav-list\" role=\"tablist\">\n ${repeat(\n this.inputData?.navItems || [],\n (item) => item.label,\n (item) => html`\n <md-secondary-tab\n ?active=${this.matchesRoute(this.resolveRoute(item))}\n @click=${() => this.handleNavItemClick(this.resolveRoute(item))}\n >\n ${item.iconName\n ? html`<md-icon slot=\"icon\">${item.iconName}</md-icon>`\n : nothing}\n ${item.label}\n </md-secondary-tab>\n `\n )}\n </div>\n </div>\n `\n }\n}\nwindow.customElements.define('widget-navbar-versionplaceholder', WidgetNavbar)\n"],"names":["isTransparent","color","value","functional","_WidgetNavbar","LitElement","changedProperties","_changedProperties","theme","cssTextColor","cssBgColor","themeBgColor","route","event","item","variable","currentSegments","routeSegments","i","itemRoute","subRoute","fontSize","fontWeight","iconFontSize","fontColor","bgColor","html","repeat","nothing","css","WidgetNavbar","__decorateClass","property","state"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "Webcomponent widget-navbar following open-wc recommendations",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "widget-navbar",
|
|
6
|
-
"version": "1.0.
|
|
6
|
+
"version": "1.0.16",
|
|
7
7
|
"engines": {
|
|
8
8
|
"node": ">=24.9.0",
|
|
9
9
|
"npm": ">=10.0.2"
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@material/web": "^2.4.1",
|
|
32
|
-
"lit": "^3.3.
|
|
32
|
+
"lit": "^3.3.3",
|
|
33
33
|
"tslib": "^2.8.1"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
@@ -79,7 +79,7 @@ export type NavigationItems = {
|
|
|
79
79
|
/**
|
|
80
80
|
* A horizontal navigation bar widget for creating top-level dashboard navigation. Use this widget to provide consistent navigation across dashboard pages with a clickable title and multiple navigation items. Each item can have an icon and routes to different dashboard sections. Supports customizable styling for font size, weight, and colors. Ideal for multi-page dashboards requiring clear navigation structure and branding.
|
|
81
81
|
*/
|
|
82
|
-
export interface
|
|
82
|
+
export interface NavigationBarConfiguration {
|
|
83
83
|
title?: TitleSettings;
|
|
84
84
|
route?: TitleNavigation;
|
|
85
85
|
variables?: TitleNavigationVariables;
|
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
{
|
|
2
|
-
"title": "
|
|
2
|
+
"title": "Navigation Bar Configuration",
|
|
3
3
|
"description": "A horizontal navigation bar widget for creating top-level dashboard navigation. Use this widget to provide consistent navigation across dashboard pages with a clickable title and multiple navigation items. Each item can have an icon and routes to different dashboard sections. Supports customizable styling for font size, weight, and colors. Ideal for multi-page dashboards requiring clear navigation structure and branding.",
|
|
4
|
+
"aiSelection": {
|
|
5
|
+
"dataShape": "Navigation structure only: a title with a route, plus navItems[] each carrying a label, an icon and a route. No data input.",
|
|
6
|
+
"useWhen": [
|
|
7
|
+
"A multi-page dashboard needs consistent top-level navigation across its pages.",
|
|
8
|
+
"A header combining branding (the title) with links to the main sections is wanted.",
|
|
9
|
+
"Route parameters should be carried along per item (variables[])."
|
|
10
|
+
],
|
|
11
|
+
"notFor": [
|
|
12
|
+
"Navigation down the side of the page — use widget-sidenav.",
|
|
13
|
+
"A single link or call to action — use widget-navbutton.",
|
|
14
|
+
"Switching what data the page shows rather than which page is shown — that is filtering. Use widget-filter-dropdown.",
|
|
15
|
+
"A heading that is only text and does not navigate — use widget-label."
|
|
16
|
+
]
|
|
17
|
+
},
|
|
4
18
|
"type": "object",
|
|
5
19
|
"properties": {
|
|
6
20
|
"title": {
|
|
@@ -69,7 +83,7 @@
|
|
|
69
83
|
"backgroundColor": {
|
|
70
84
|
"title": "Background Color",
|
|
71
85
|
"description": "Background color of the navigation bar.",
|
|
72
|
-
"order":
|
|
86
|
+
"order": 5,
|
|
73
87
|
"type": "color",
|
|
74
88
|
"dataDrivenDisabled": true
|
|
75
89
|
}
|
package/src/widget-navbar.ts
CHANGED
|
@@ -1,19 +1,36 @@
|
|
|
1
1
|
import { html, css, LitElement, PropertyValues, nothing } from 'lit'
|
|
2
2
|
import { property, state } from 'lit/decorators.js'
|
|
3
3
|
import { repeat } from 'lit/directives/repeat.js'
|
|
4
|
-
import {
|
|
4
|
+
import { NavigationBarConfiguration } from './definition-schema'
|
|
5
5
|
|
|
6
6
|
import '@material/web/icon/icon'
|
|
7
|
-
import '@material/web/tabs/tabs'
|
|
8
|
-
import '@material/web/tabs/primary-tab'
|
|
9
7
|
import '@material/web/tabs/secondary-tab'
|
|
10
8
|
|
|
11
9
|
type Theme = {
|
|
12
10
|
theme_name: string
|
|
13
11
|
theme_object: any
|
|
14
12
|
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* ECharts themes describe a *chart canvas*, and most of them set
|
|
16
|
+
* `backgroundColor` to a fully transparent colour (the light theme uses
|
|
17
|
+
* `rgba(0, 0, 0, 0)`). That is a valid, truthy colour string, so using it
|
|
18
|
+
* verbatim as the navbar background paints nothing at all. Treat any
|
|
19
|
+
* fully transparent theme colour as "no colour" so the fallback kicks in.
|
|
20
|
+
*/
|
|
21
|
+
const isTransparent = (color?: string) => {
|
|
22
|
+
if (!color) return true
|
|
23
|
+
const value = color.trim().toLowerCase()
|
|
24
|
+
if (value === 'transparent') return true
|
|
25
|
+
const functional = value.match(/^(?:rgba|hsla)\([^)]*[,/]\s*([\d.]+%?)\s*\)$/)
|
|
26
|
+
if (functional) return parseFloat(functional[1]) === 0
|
|
27
|
+
// #rrggbbaa / #rgba
|
|
28
|
+
if (/^#[0-9a-f]{8}$/.test(value)) return value.slice(7) === '00'
|
|
29
|
+
if (/^#[0-9a-f]{4}$/.test(value)) return value[4] === '0'
|
|
30
|
+
return false
|
|
31
|
+
}
|
|
15
32
|
export class WidgetNavbar extends LitElement {
|
|
16
|
-
@property({ type: Object }) inputData?:
|
|
33
|
+
@property({ type: Object }) inputData?: NavigationBarConfiguration
|
|
17
34
|
@property({ type: Object }) theme?: Theme
|
|
18
35
|
@property({ type: String }) route?: string
|
|
19
36
|
|
|
@@ -37,8 +54,9 @@ export class WidgetNavbar extends LitElement {
|
|
|
37
54
|
registerTheme(theme?: Theme) {
|
|
38
55
|
const cssTextColor = getComputedStyle(this).getPropertyValue('--re-text-color').trim()
|
|
39
56
|
const cssBgColor = getComputedStyle(this).getPropertyValue('--re-tile-background-color').trim()
|
|
40
|
-
|
|
41
|
-
this.
|
|
57
|
+
const themeBgColor = theme?.theme_object?.backgroundColor
|
|
58
|
+
this.themeBgColor = cssBgColor || (isTransparent(themeBgColor) ? undefined : themeBgColor)
|
|
59
|
+
this.themeTitleColor = cssTextColor || theme?.theme_object?.title?.textStyle?.color
|
|
42
60
|
}
|
|
43
61
|
|
|
44
62
|
handleNavItemClick(route?: string) {
|
|
@@ -71,27 +89,83 @@ export class WidgetNavbar extends LitElement {
|
|
|
71
89
|
.wrapper {
|
|
72
90
|
display: flex;
|
|
73
91
|
align-items: end;
|
|
92
|
+
gap: 8px;
|
|
74
93
|
height: 100%;
|
|
75
94
|
width: 100%;
|
|
76
95
|
padding: 0 16px;
|
|
77
96
|
box-sizing: border-box;
|
|
97
|
+
background-color: var(--nav-bg);
|
|
98
|
+
color: var(--nav-fg);
|
|
99
|
+
font-size: var(--nav-font-size);
|
|
100
|
+
font-weight: var(--nav-font-weight);
|
|
78
101
|
}
|
|
79
102
|
|
|
80
|
-
|
|
81
|
-
|
|
103
|
+
/* One scrollable row. Without this the tabs wrap onto several lines and
|
|
104
|
+
spill outside the coloured bar as soon as they don't fit. */
|
|
105
|
+
.nav-list {
|
|
106
|
+
display: flex;
|
|
107
|
+
align-items: end;
|
|
108
|
+
flex: 1;
|
|
109
|
+
min-width: 0;
|
|
110
|
+
overflow-x: auto;
|
|
111
|
+
scrollbar-width: none;
|
|
82
112
|
}
|
|
83
113
|
|
|
84
|
-
.
|
|
85
|
-
|
|
86
|
-
|
|
114
|
+
.nav-list::-webkit-scrollbar {
|
|
115
|
+
display: none;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/* Material's tab tokens default to the M3 palette (purple indicator,
|
|
119
|
+
near-black labels), which ignores the widget's configured colours.
|
|
120
|
+
Map every colour token onto the nav colours. */
|
|
121
|
+
md-secondary-tab {
|
|
122
|
+
flex: none;
|
|
123
|
+
--md-secondary-tab-container-color: var(--nav-bg);
|
|
124
|
+
--md-secondary-tab-label-text-size: var(--nav-font-size);
|
|
125
|
+
--md-secondary-tab-label-text-weight: var(--nav-font-weight);
|
|
126
|
+
--md-secondary-tab-icon-size: var(--nav-icon-size);
|
|
127
|
+
--md-secondary-tab-label-text-color: var(--nav-fg);
|
|
128
|
+
--md-secondary-tab-hover-label-text-color: var(--nav-fg);
|
|
129
|
+
--md-secondary-tab-focus-label-text-color: var(--nav-fg);
|
|
130
|
+
--md-secondary-tab-pressed-label-text-color: var(--nav-fg);
|
|
131
|
+
--md-secondary-tab-active-label-text-color: var(--nav-fg);
|
|
132
|
+
--md-secondary-tab-active-hover-label-text-color: var(--nav-fg);
|
|
133
|
+
--md-secondary-tab-active-focus-label-text-color: var(--nav-fg);
|
|
134
|
+
--md-secondary-tab-active-pressed-label-text-color: var(--nav-fg);
|
|
135
|
+
--md-secondary-tab-icon-color: var(--nav-fg);
|
|
136
|
+
--md-secondary-tab-hover-icon-color: var(--nav-fg);
|
|
137
|
+
--md-secondary-tab-focus-icon-color: var(--nav-fg);
|
|
138
|
+
--md-secondary-tab-pressed-icon-color: var(--nav-fg);
|
|
139
|
+
--md-secondary-tab-active-icon-color: var(--nav-fg);
|
|
140
|
+
--md-secondary-tab-active-hover-icon-color: var(--nav-fg);
|
|
141
|
+
--md-secondary-tab-active-focus-icon-color: var(--nav-fg);
|
|
142
|
+
--md-secondary-tab-active-pressed-icon-color: var(--nav-fg);
|
|
143
|
+
--md-secondary-tab-active-indicator-color: var(--nav-fg);
|
|
144
|
+
--md-secondary-tab-hover-state-layer-color: var(--nav-fg);
|
|
145
|
+
--md-secondary-tab-pressed-state-layer-color: var(--nav-fg);
|
|
146
|
+
--md-secondary-tab-active-hover-state-layer-color: var(--nav-fg);
|
|
147
|
+
--md-secondary-tab-active-pressed-state-layer-color: var(--nav-fg);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
md-icon {
|
|
151
|
+
font-family: 'Material Symbols Outlined';
|
|
152
|
+
font-size: var(--nav-icon-size);
|
|
153
|
+
width: var(--nav-icon-size);
|
|
154
|
+
height: var(--nav-icon-size);
|
|
155
|
+
color: var(--nav-fg);
|
|
87
156
|
}
|
|
88
157
|
|
|
89
158
|
h2 {
|
|
90
159
|
margin: 0;
|
|
91
160
|
padding: 0px;
|
|
161
|
+
flex: none;
|
|
162
|
+
display: flex;
|
|
163
|
+
align-items: center;
|
|
164
|
+
min-height: 48px;
|
|
92
165
|
font-size: 1.2em;
|
|
93
|
-
|
|
94
|
-
|
|
166
|
+
/* the UA stylesheet forces bold, which would ignore the
|
|
167
|
+
configured font weight */
|
|
168
|
+
font-weight: inherit;
|
|
95
169
|
}
|
|
96
170
|
`
|
|
97
171
|
|
|
@@ -137,15 +211,16 @@ export class WidgetNavbar extends LitElement {
|
|
|
137
211
|
|
|
138
212
|
render() {
|
|
139
213
|
const fontSize = this.inputData?.style?.fontSize ?? 16
|
|
140
|
-
const
|
|
214
|
+
const fontWeight = this.inputData?.style?.fontWeight ?? 400
|
|
215
|
+
const iconFontSize = fontSize * 1.5
|
|
141
216
|
const fontColor = this.inputData?.style?.color || this.themeTitleColor || 'black'
|
|
142
217
|
const bgColor = this.inputData?.style?.backgroundColor || this.themeBgColor || 'white'
|
|
143
218
|
return html`
|
|
144
219
|
<div
|
|
145
220
|
class="wrapper"
|
|
146
|
-
style="
|
|
147
|
-
font-weight: ${
|
|
148
|
-
|
|
221
|
+
style="--nav-bg: ${bgColor}; --nav-fg: ${fontColor};
|
|
222
|
+
--nav-font-size: ${fontSize}px; --nav-font-weight: ${fontWeight};
|
|
223
|
+
--nav-icon-size: ${iconFontSize}px;"
|
|
149
224
|
>
|
|
150
225
|
<h2
|
|
151
226
|
class="paging"
|
|
@@ -161,33 +236,23 @@ export class WidgetNavbar extends LitElement {
|
|
|
161
236
|
>
|
|
162
237
|
${this.inputData?.title}
|
|
163
238
|
</h2>
|
|
164
|
-
<
|
|
239
|
+
<div class="nav-list" role="tablist">
|
|
165
240
|
${repeat(
|
|
166
241
|
this.inputData?.navItems || [],
|
|
167
242
|
(item) => item.label,
|
|
168
243
|
(item) => html`
|
|
169
244
|
<md-secondary-tab
|
|
170
245
|
?active=${this.matchesRoute(this.resolveRoute(item))}
|
|
171
|
-
style="font-size: ${fontSize}px;
|
|
172
|
-
--md-secondary-tab-container-color: ${bgColor};
|
|
173
|
-
color: ${fontColor};"
|
|
174
246
|
@click=${() => this.handleNavItemClick(this.resolveRoute(item))}
|
|
175
247
|
>
|
|
176
248
|
${item.iconName
|
|
177
|
-
? html
|
|
178
|
-
<md-icon
|
|
179
|
-
slot="icon"
|
|
180
|
-
style="font-size: ${iconFontSize}px;width: ${iconFontSize}px;color: ${fontColor}"
|
|
181
|
-
>
|
|
182
|
-
${item.iconName}
|
|
183
|
-
</md-icon>
|
|
184
|
-
`
|
|
249
|
+
? html`<md-icon slot="icon">${item.iconName}</md-icon>`
|
|
185
250
|
: nothing}
|
|
186
251
|
${item.label}
|
|
187
252
|
</md-secondary-tab>
|
|
188
253
|
`
|
|
189
254
|
)}
|
|
190
|
-
</
|
|
255
|
+
</div>
|
|
191
256
|
</div>
|
|
192
257
|
`
|
|
193
258
|
}
|