@mp-consulting/homebridge-ui-kit 1.0.1 → 1.1.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.
- package/README.md +22 -5
- package/dist/kit.css +48 -9
- package/dist/kit.js +84 -40
- package/package.json +17 -2
package/README.md
CHANGED
|
@@ -52,21 +52,23 @@ The plugin's `homebridge-ui/public/index.html` must be a full HTML document. Loa
|
|
|
52
52
|
} catch(e) {}
|
|
53
53
|
})();
|
|
54
54
|
</script>
|
|
55
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css">
|
|
56
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
|
55
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
|
56
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" integrity="sha384-XGjxtQfXaH2tnPFa9x+ruJTuLE3Aa6LhHSWRr1XeTyhezb4abCG4ccI5AkVDxqC+" crossorigin="anonymous">
|
|
57
57
|
<link rel="stylesheet" href="kit.css">
|
|
58
58
|
<link rel="stylesheet" href="styles.css">
|
|
59
59
|
</head>
|
|
60
60
|
<body>
|
|
61
61
|
<!-- your UI here -->
|
|
62
62
|
|
|
63
|
-
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
|
63
|
+
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
|
|
64
64
|
<script src="kit.js"></script>
|
|
65
65
|
<script src="app.js"></script>
|
|
66
66
|
</body>
|
|
67
67
|
</html>
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
+
The `integrity` hashes pin the exact CDN files; update them if you change a version (jsDelivr shows the SRI hash for each file).
|
|
71
|
+
|
|
70
72
|
The early inline script applies `data-bs-theme="dark"` from the system preference before any CSS loads, preventing a flash of wrong theme. In your app script, also apply the Homebridge user's saved theme setting:
|
|
71
73
|
|
|
72
74
|
```js
|
|
@@ -90,7 +92,7 @@ homebridge-ui/public/kit.js
|
|
|
90
92
|
|
|
91
93
|
### 5. Update `eslint.config.js`
|
|
92
94
|
|
|
93
|
-
Add `kit.js` to the ignore list (it is a
|
|
95
|
+
Add `kit.js` to the ignore list (it is a generated, vendored file) and expose `MpKit` as a browser global:
|
|
94
96
|
|
|
95
97
|
```js
|
|
96
98
|
export default tseslint.config(
|
|
@@ -127,8 +129,18 @@ MpKit.Loading.render('Loading...')
|
|
|
127
129
|
|
|
128
130
|
// View switching (.mp-view elements)
|
|
129
131
|
MpKit.View.show('viewId')
|
|
132
|
+
|
|
133
|
+
// Support footer (only http/https URLs are rendered; icons are inline SVG,
|
|
134
|
+
// so the Bootstrap Icons font is not required)
|
|
135
|
+
MpKit.Footer.render({ github, npm, changelog, target: '.mp-footer' })
|
|
136
|
+
|
|
137
|
+
// Escape untrusted text before interpolating it into your own HTML
|
|
138
|
+
MpKit.escapeHtml(device.name)
|
|
130
139
|
```
|
|
131
140
|
|
|
141
|
+
All text passed to the helpers is HTML-escaped, so it is safe to pass device names or other
|
|
142
|
+
values that came from the network. To include markup, build the element yourself.
|
|
143
|
+
|
|
132
144
|
## Dark Mode
|
|
133
145
|
|
|
134
146
|
Dark mode is handled entirely by Bootstrap's `data-bs-theme="dark"` attribute on `<html>`. Do **not** use `@media (prefers-color-scheme: dark)` blocks, `.dark-mode` CSS classes, or custom CSS variable overrides — Bootstrap handles all of this automatically. Use Bootstrap CSS variables (`var(--bs-body-bg)`, `var(--bs-primary)`, etc.) in your custom CSS instead of hardcoded hex values.
|
|
@@ -137,9 +149,14 @@ Dark mode is handled entirely by Bootstrap's `data-bs-theme="dark"` attribute on
|
|
|
137
149
|
|
|
138
150
|
```bash
|
|
139
151
|
npm install
|
|
140
|
-
npm run build
|
|
152
|
+
npm run build # outputs to dist/ (commit the result)
|
|
153
|
+
npm run build:check # fails if dist/ is out of date (run in CI)
|
|
154
|
+
npm test
|
|
141
155
|
```
|
|
142
156
|
|
|
157
|
+
Use the `--mp-*` tokens for brand colors. For text in the brand color use `--mp-primary-text`,
|
|
158
|
+
which switches to a lighter shade in dark mode so it keeps WCAG AA contrast.
|
|
159
|
+
|
|
143
160
|
## License
|
|
144
161
|
|
|
145
162
|
MIT © MP Consulting
|
package/dist/kit.css
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
/* @mp-consulting/homebridge-ui-kit v1.0
|
|
1
|
+
/* @mp-consulting/homebridge-ui-kit v1.1.0 */
|
|
2
2
|
|
|
3
3
|
/* ── Design tokens ──────────────────────────────────────────────────────────
|
|
4
4
|
CSS custom properties used throughout the design system.
|
|
5
5
|
Override these in your plugin's styles.css to customize per-plugin.
|
|
6
|
+
Light values live on :root; dark values follow Bootstrap's data-bs-theme.
|
|
6
7
|
────────────────────────────────────────────────────────────────────────── */
|
|
7
8
|
|
|
8
9
|
:root {
|
|
@@ -10,7 +11,7 @@
|
|
|
10
11
|
--mp-primary: #4f46e5;
|
|
11
12
|
--mp-primary-rgb: 79, 70, 229;
|
|
12
13
|
--mp-primary-hover: #4338ca;
|
|
13
|
-
--mp-primary-
|
|
14
|
+
--mp-primary-text: #4f46e5; /* brand color for text; meets WCAG AA on the body bg */
|
|
14
15
|
|
|
15
16
|
/* Status */
|
|
16
17
|
--mp-status-online: #10b981;
|
|
@@ -19,11 +20,18 @@
|
|
|
19
20
|
--mp-status-checking: #94a3b8;
|
|
20
21
|
|
|
21
22
|
/* Surfaces */
|
|
22
|
-
--mp-surface: rgba(
|
|
23
|
-
--mp-border: rgba(
|
|
23
|
+
--mp-surface: rgba(0, 0, 0, 0.02);
|
|
24
|
+
--mp-border: rgba(0, 0, 0, 0.1);
|
|
24
25
|
|
|
25
26
|
/* Shape */
|
|
26
27
|
--mp-radius: 0.5rem;
|
|
28
|
+
--mp-shadow-hover: 0 0.5rem 1rem rgba(0, 0, 0, 0.12);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
[data-bs-theme="dark"] {
|
|
32
|
+
--mp-primary-text: #818cf8;
|
|
33
|
+
--mp-surface: rgba(255, 255, 255, 0.03);
|
|
34
|
+
--mp-border: rgba(255, 255, 255, 0.08);
|
|
27
35
|
--mp-shadow-hover: 0 0.5rem 1rem rgba(0, 0, 0, 0.3);
|
|
28
36
|
}
|
|
29
37
|
|
|
@@ -40,6 +48,8 @@
|
|
|
40
48
|
--bs-btn-active-bg: var(--mp-primary-hover);
|
|
41
49
|
--bs-btn-active-border-color: var(--mp-primary-hover);
|
|
42
50
|
--bs-btn-focus-shadow-rgb: var(--mp-primary-rgb);
|
|
51
|
+
--bs-btn-disabled-bg: var(--mp-primary);
|
|
52
|
+
--bs-btn-disabled-border-color: var(--mp-primary);
|
|
43
53
|
}
|
|
44
54
|
|
|
45
55
|
/* Primary spinner */
|
|
@@ -122,6 +132,11 @@
|
|
|
122
132
|
box-shadow: var(--mp-shadow-hover) !important;
|
|
123
133
|
}
|
|
124
134
|
|
|
135
|
+
.mp-device-card:focus-visible {
|
|
136
|
+
outline: 2px solid var(--mp-primary);
|
|
137
|
+
outline-offset: 2px;
|
|
138
|
+
}
|
|
139
|
+
|
|
125
140
|
/* ── Settings surface card ──────────────────────────────── */
|
|
126
141
|
|
|
127
142
|
.mp-settings-card {
|
|
@@ -184,20 +199,20 @@
|
|
|
184
199
|
.mp-tabs .nav-link {
|
|
185
200
|
color: var(--bs-secondary-color);
|
|
186
201
|
border: none;
|
|
202
|
+
border-bottom: 2px solid transparent; /* reserve space so the active tab doesn't shift */
|
|
187
203
|
padding: 0.75rem 1.25rem;
|
|
188
204
|
font-weight: 500;
|
|
189
205
|
}
|
|
190
206
|
|
|
191
207
|
.mp-tabs .nav-link:hover {
|
|
192
208
|
color: var(--bs-body-color);
|
|
193
|
-
border:
|
|
209
|
+
border-color: transparent;
|
|
194
210
|
}
|
|
195
211
|
|
|
196
212
|
.mp-tabs .nav-link.active {
|
|
197
|
-
color: var(--mp-primary);
|
|
213
|
+
color: var(--mp-primary-text);
|
|
198
214
|
background: transparent;
|
|
199
|
-
border:
|
|
200
|
-
border-bottom: 2px solid var(--mp-primary);
|
|
215
|
+
border-bottom-color: var(--mp-primary);
|
|
201
216
|
}
|
|
202
217
|
|
|
203
218
|
/* ── Metadata label ─────────────────────────────────────── */
|
|
@@ -244,10 +259,34 @@
|
|
|
244
259
|
}
|
|
245
260
|
|
|
246
261
|
.mp-footer a:hover {
|
|
247
|
-
color: var(--mp-primary);
|
|
262
|
+
color: var(--mp-primary-text);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
.mp-footer-icon {
|
|
266
|
+
width: 1em;
|
|
267
|
+
height: 1em;
|
|
268
|
+
flex-shrink: 0;
|
|
269
|
+
fill: currentColor;
|
|
248
270
|
}
|
|
249
271
|
|
|
250
272
|
.mp-footer-sep {
|
|
251
273
|
color: var(--mp-border);
|
|
252
274
|
user-select: none;
|
|
253
275
|
}
|
|
276
|
+
|
|
277
|
+
/* ── Reduced motion ─────────────────────────────────────── */
|
|
278
|
+
|
|
279
|
+
@media (prefers-reduced-motion: reduce) {
|
|
280
|
+
.mp-status-checking {
|
|
281
|
+
animation: none;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
.mp-device-card,
|
|
285
|
+
.mp-footer a {
|
|
286
|
+
transition: none;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
.mp-device-card:hover {
|
|
290
|
+
transform: none;
|
|
291
|
+
}
|
|
292
|
+
}
|
package/dist/kit.js
CHANGED
|
@@ -1,47 +1,96 @@
|
|
|
1
|
-
/* @mp-consulting/homebridge-ui-kit v1.
|
|
1
|
+
/* @mp-consulting/homebridge-ui-kit v1.1.0
|
|
2
2
|
Brand design system for Homebridge plugins
|
|
3
3
|
https://github.com/mp-consulting/homebridge-ui-kit */
|
|
4
4
|
|
|
5
5
|
(function (global) {
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
|
+
var ESCAPES = { '&': '&', '<': '<', '>': '>', '"': '"', '\'': ''' };
|
|
9
|
+
|
|
10
|
+
/** Escapes a value for safe use in HTML text and quoted attributes. */
|
|
11
|
+
function escapeHtml(value) {
|
|
12
|
+
return String(value == null ? '' : value).replace(/[&<>"']/g, function (c) {
|
|
13
|
+
return ESCAPES[c];
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Returns the URL if it uses http(s), otherwise null (blocks javascript:, data:, …). */
|
|
18
|
+
function safeUrl(url) {
|
|
19
|
+
if (typeof url !== 'string') { return null; }
|
|
20
|
+
return /^https?:\/\//i.test(url.trim()) ? url.trim() : null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function badge(badgeClass, dotClass, label) {
|
|
24
|
+
return '<span class="badge ' + badgeClass + '">'
|
|
25
|
+
+ (dotClass ? '<span class="mp-status ' + dotClass + ' me-1" aria-hidden="true"></span>' : '')
|
|
26
|
+
+ escapeHtml(label) + '</span>';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Icon paths from Bootstrap Icons 1.11.3 (MIT), inlined so the footer
|
|
30
|
+
// works whether or not the plugin loads the icon font.
|
|
31
|
+
var FOOTER_LINKS = [
|
|
32
|
+
{
|
|
33
|
+
key: 'github',
|
|
34
|
+
text: 'GitHub',
|
|
35
|
+
paths: [
|
|
36
|
+
'M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8',
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
key: 'npm',
|
|
41
|
+
text: 'npm',
|
|
42
|
+
paths: [
|
|
43
|
+
'M8.186 1.113a.5.5 0 0 0-.372 0L1.846 3.5l2.404.961L10.404 2zm3.564 1.426L5.596 5 8 5.961 14.154 3.5zm3.25 1.7-6.5 2.6v7.922l6.5-2.6V4.24zM7.5 14.762V6.838L1 4.239v7.923zM7.443.184a1.5 1.5 0 0 1 1.114 0l7.129 2.852A.5.5 0 0 1 16 3.5v8.662a1 1 0 0 1-.629.928l-7.185 2.874a.5.5 0 0 1-.372 0L.63 13.09a1 1 0 0 1-.63-.928V3.5a.5.5 0 0 1 .314-.464z',
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
key: 'changelog',
|
|
48
|
+
text: 'Changelog',
|
|
49
|
+
paths: [
|
|
50
|
+
'M8.515 1.019A7 7 0 0 0 8 1V0a8 8 0 0 1 .589.022zm2.004.45a7 7 0 0 0-.985-.299l.219-.976q.576.129 1.126.342zm1.37.71a7 7 0 0 0-.439-.27l.493-.87a8 8 0 0 1 .979.654l-.615.789a7 7 0 0 0-.418-.302zm1.834 1.79a7 7 0 0 0-.653-.796l.724-.69q.406.429.747.91zm.744 1.352a7 7 0 0 0-.214-.468l.893-.45a8 8 0 0 1 .45 1.088l-.95.313a7 7 0 0 0-.179-.483m.53 2.507a7 7 0 0 0-.1-1.025l.985-.17q.1.58.116 1.17zm-.131 1.538q.05-.254.081-.51l.993.123a8 8 0 0 1-.23 1.155l-.964-.267q.069-.247.12-.501m-.952 2.379q.276-.436.486-.908l.914.405q-.24.54-.555 1.038zm-.964 1.205q.183-.183.35-.378l.758.653a8 8 0 0 1-.401.432z',
|
|
51
|
+
'M8 1a7 7 0 1 0 4.95 11.95l.707.707A8.001 8.001 0 1 1 8 0z',
|
|
52
|
+
'M7.5 3a.5.5 0 0 1 .5.5v5.21l3.248 1.856a.5.5 0 0 1-.496.868l-3.5-2A.5.5 0 0 1 7 9V3.5a.5.5 0 0 1 .5-.5',
|
|
53
|
+
],
|
|
54
|
+
},
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
function icon(paths) {
|
|
58
|
+
return '<svg class="mp-footer-icon" viewBox="0 0 16 16" aria-hidden="true" focusable="false">'
|
|
59
|
+
+ paths.map(function (d) { return '<path d="' + d + '"/>'; }).join('')
|
|
60
|
+
+ '</svg>';
|
|
61
|
+
}
|
|
62
|
+
|
|
8
63
|
var MpKit = {
|
|
9
64
|
|
|
65
|
+
escapeHtml: escapeHtml,
|
|
66
|
+
|
|
10
67
|
/**
|
|
11
68
|
* StatusBadge — returns inline HTML for device status badges.
|
|
69
|
+
* Labels are HTML-escaped.
|
|
12
70
|
*
|
|
13
71
|
* MpKit.StatusBadge.online() → green dot + "Online"
|
|
14
72
|
* MpKit.StatusBadge.offline() → red dot + "Offline"
|
|
15
|
-
* MpKit.StatusBadge.checking() → animated dot + "Checking
|
|
73
|
+
* MpKit.StatusBadge.checking() → animated dot + "Checking…"
|
|
16
74
|
* MpKit.StatusBadge.disabled() → grey badge + "Disabled"
|
|
17
75
|
*/
|
|
18
76
|
StatusBadge: {
|
|
19
77
|
online: function (label) {
|
|
20
|
-
|
|
21
|
-
return '<span class="badge bg-success-subtle text-success">'
|
|
22
|
-
+ '<span class="mp-status mp-status-online me-1"></span>'
|
|
23
|
-
+ label + '</span>';
|
|
78
|
+
return badge('bg-success-subtle text-success', 'mp-status-online', label || 'Online');
|
|
24
79
|
},
|
|
25
80
|
offline: function (label) {
|
|
26
|
-
|
|
27
|
-
return '<span class="badge bg-danger-subtle text-danger">'
|
|
28
|
-
+ '<span class="mp-status mp-status-offline me-1"></span>'
|
|
29
|
-
+ label + '</span>';
|
|
81
|
+
return badge('bg-danger-subtle text-danger', 'mp-status-offline', label || 'Offline');
|
|
30
82
|
},
|
|
31
83
|
checking: function (label) {
|
|
32
|
-
|
|
33
|
-
return '<span class="badge bg-secondary-subtle text-secondary">'
|
|
34
|
-
+ '<span class="mp-status mp-status-checking me-1"></span>'
|
|
35
|
-
+ label + '</span>';
|
|
84
|
+
return badge('bg-secondary-subtle text-secondary', 'mp-status-checking', label || 'Checking…');
|
|
36
85
|
},
|
|
37
86
|
disabled: function (label) {
|
|
38
|
-
|
|
39
|
-
return '<span class="badge bg-secondary">' + label + '</span>';
|
|
87
|
+
return badge('bg-secondary', null, label || 'Disabled');
|
|
40
88
|
},
|
|
41
89
|
},
|
|
42
90
|
|
|
43
91
|
/**
|
|
44
92
|
* EmptyState — returns HTML for an empty list placeholder.
|
|
93
|
+
* All options are HTML-escaped.
|
|
45
94
|
*
|
|
46
95
|
* MpKit.EmptyState.render({
|
|
47
96
|
* iconClass: 'bi bi-lightbulb',
|
|
@@ -56,24 +105,25 @@
|
|
|
56
105
|
var title = opts.title || 'No items';
|
|
57
106
|
var hint = opts.hint || '';
|
|
58
107
|
return '<div class="mp-empty-state">'
|
|
59
|
-
+ '<i class="' + icon + ' mp-empty-state-icon"></i>'
|
|
60
|
-
+ '<p class="mp-empty-state-title">' + title + '</p>'
|
|
61
|
-
+ (hint ? '<p class="mp-empty-state-hint">' + hint + '</p>' : '')
|
|
108
|
+
+ '<i class="' + escapeHtml(icon) + ' mp-empty-state-icon" aria-hidden="true"></i>'
|
|
109
|
+
+ '<p class="mp-empty-state-title">' + escapeHtml(title) + '</p>'
|
|
110
|
+
+ (hint ? '<p class="mp-empty-state-hint">' + escapeHtml(hint) + '</p>' : '')
|
|
62
111
|
+ '</div>';
|
|
63
112
|
},
|
|
64
113
|
},
|
|
65
114
|
|
|
66
115
|
/**
|
|
67
116
|
* Loading — returns HTML for a centred loading spinner with message.
|
|
117
|
+
* The message is HTML-escaped and announced to screen readers.
|
|
68
118
|
*
|
|
69
119
|
* MpKit.Loading.render('Loading device information...')
|
|
70
120
|
*/
|
|
71
121
|
Loading: {
|
|
72
122
|
render: function (message) {
|
|
73
|
-
message = message || 'Loading
|
|
74
|
-
return '<div class="mp-loading">'
|
|
75
|
-
+ '<div class="spinner-border spinner-border-sm text-secondary"
|
|
76
|
-
+ '<span>' + message + '</span>'
|
|
123
|
+
message = message || 'Loading…';
|
|
124
|
+
return '<div class="mp-loading" role="status" aria-live="polite">'
|
|
125
|
+
+ '<div class="spinner-border spinner-border-sm text-secondary" aria-hidden="true"></div>'
|
|
126
|
+
+ '<span>' + escapeHtml(message) + '</span>'
|
|
77
127
|
+ '</div>';
|
|
78
128
|
},
|
|
79
129
|
},
|
|
@@ -87,20 +137,20 @@
|
|
|
87
137
|
View: {
|
|
88
138
|
show: function (id) {
|
|
89
139
|
document.querySelectorAll('.mp-view').forEach(function (v) {
|
|
90
|
-
v.classList.
|
|
140
|
+
v.classList.toggle('active', v.id === id);
|
|
91
141
|
});
|
|
92
|
-
var el = document.getElementById(id);
|
|
93
|
-
if (el) { el.classList.add('active'); }
|
|
94
142
|
},
|
|
95
143
|
},
|
|
96
144
|
|
|
97
145
|
/**
|
|
98
146
|
* Footer — renders support links into a .mp-footer element.
|
|
147
|
+
* Only http(s) URLs are rendered; anything else is skipped.
|
|
99
148
|
*
|
|
100
149
|
* MpKit.Footer.render({
|
|
101
150
|
* github: 'https://github.com/mp-consulting/homebridge-...',
|
|
102
151
|
* npm: 'https://www.npmjs.com/package/@mp-consulting/...',
|
|
103
152
|
* changelog: 'https://github.com/.../blob/main/CHANGELOG.md',
|
|
153
|
+
* target: '.mp-footer', // optional selector or element
|
|
104
154
|
* })
|
|
105
155
|
*/
|
|
106
156
|
Footer: {
|
|
@@ -110,19 +160,13 @@
|
|
|
110
160
|
var el = typeof target === 'string' ? document.querySelector(target) : target;
|
|
111
161
|
if (!el) { return; }
|
|
112
162
|
var links = [];
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
121
|
-
if (opts.changelog) {
|
|
122
|
-
links.push('<a href="' + opts.changelog + '" target="_blank" rel="noopener">'
|
|
123
|
-
+ '<i class="bi bi-clock-history"></i>Changelog</a>');
|
|
124
|
-
}
|
|
125
|
-
el.innerHTML = links.join('<span class="mp-footer-sep">|</span>');
|
|
163
|
+
FOOTER_LINKS.forEach(function (link) {
|
|
164
|
+
var url = safeUrl(opts[link.key]);
|
|
165
|
+
if (!url) { return; }
|
|
166
|
+
links.push('<a href="' + escapeHtml(url) + '" target="_blank" rel="noopener noreferrer">'
|
|
167
|
+
+ icon(link.paths) + link.text + '</a>');
|
|
168
|
+
});
|
|
169
|
+
el.innerHTML = links.join('<span class="mp-footer-sep" aria-hidden="true">|</span>');
|
|
126
170
|
},
|
|
127
171
|
},
|
|
128
172
|
|
|
@@ -130,4 +174,4 @@
|
|
|
130
174
|
|
|
131
175
|
global.MpKit = MpKit;
|
|
132
176
|
|
|
133
|
-
})(window);
|
|
177
|
+
})(typeof window !== 'undefined' ? window : globalThis);
|
package/package.json
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mp-consulting/homebridge-ui-kit",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Brand design system for @mp-consulting Homebridge plugins",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"homebridge",
|
|
7
|
+
"homebridge-ui",
|
|
8
|
+
"bootstrap",
|
|
9
|
+
"design-system"
|
|
10
|
+
],
|
|
11
|
+
"homepage": "https://github.com/mp-consulting/homebridge-ui-kit#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/mp-consulting/homebridge-ui-kit/issues"
|
|
14
|
+
},
|
|
5
15
|
"author": "MP Consulting",
|
|
6
16
|
"license": "MIT",
|
|
7
17
|
"type": "module",
|
|
@@ -12,7 +22,12 @@
|
|
|
12
22
|
"files": [
|
|
13
23
|
"dist"
|
|
14
24
|
],
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=20"
|
|
27
|
+
},
|
|
15
28
|
"scripts": {
|
|
16
|
-
"build": "node scripts/build.js"
|
|
29
|
+
"build": "node scripts/build.js",
|
|
30
|
+
"build:check": "node scripts/build.js --check",
|
|
31
|
+
"test": "node --test test/*.test.js"
|
|
17
32
|
}
|
|
18
33
|
}
|