@rmdes/indiekit-endpoint-homepage 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +32 -0
- package/package.json +1 -1
- package/views/homepage-dashboard.njk +74 -11
package/index.js
CHANGED
|
@@ -138,6 +138,38 @@ export default class HomepageEndpoint {
|
|
|
138
138
|
defaultConfig: {},
|
|
139
139
|
configSchema: {},
|
|
140
140
|
},
|
|
141
|
+
{
|
|
142
|
+
id: "social-activity",
|
|
143
|
+
label: "Social Activity",
|
|
144
|
+
description: "Bluesky and Mastodon feeds",
|
|
145
|
+
icon: "message-circle",
|
|
146
|
+
defaultConfig: {},
|
|
147
|
+
configSchema: {},
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
id: "github-repos",
|
|
151
|
+
label: "GitHub Projects",
|
|
152
|
+
description: "GitHub repositories and activity",
|
|
153
|
+
icon: "github",
|
|
154
|
+
defaultConfig: {},
|
|
155
|
+
configSchema: {},
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
id: "funkwhale",
|
|
159
|
+
label: "Listening",
|
|
160
|
+
description: "Funkwhale now playing and stats",
|
|
161
|
+
icon: "music",
|
|
162
|
+
defaultConfig: {},
|
|
163
|
+
configSchema: {},
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
id: "blogroll",
|
|
167
|
+
label: "Blogroll",
|
|
168
|
+
description: "Blog recommendations",
|
|
169
|
+
icon: "list",
|
|
170
|
+
defaultConfig: {},
|
|
171
|
+
configSchema: {},
|
|
172
|
+
},
|
|
141
173
|
];
|
|
142
174
|
}
|
|
143
175
|
|
package/package.json
CHANGED
|
@@ -284,10 +284,67 @@
|
|
|
284
284
|
</form>
|
|
285
285
|
|
|
286
286
|
<script>
|
|
287
|
+
// Label lookup maps for rendering
|
|
288
|
+
const sectionLabels = {
|
|
289
|
+
{% for section in sections %}'{{ section.id }}': '{{ section.label }}'{% if not loop.last %}, {% endif %}{% endfor %}
|
|
290
|
+
};
|
|
291
|
+
const widgetLabels = {
|
|
292
|
+
{% for widget in widgets %}'{{ widget.id }}': '{{ widget.label }}'{% if not loop.last %}, {% endif %}{% endfor %}
|
|
293
|
+
};
|
|
294
|
+
|
|
287
295
|
// Parse current sections and sidebar from hidden inputs
|
|
288
296
|
let sections = JSON.parse(document.getElementById('sections-json').value || '[]');
|
|
289
297
|
let sidebar = JSON.parse(document.getElementById('sidebar-json').value || '[]');
|
|
290
298
|
|
|
299
|
+
function createItemElement(type, labels, removeFn) {
|
|
300
|
+
const li = document.createElement('li');
|
|
301
|
+
li.className = 'hp-section-item';
|
|
302
|
+
li.dataset.type = type;
|
|
303
|
+
|
|
304
|
+
const info = document.createElement('div');
|
|
305
|
+
info.className = 'hp-section-item__info';
|
|
306
|
+
|
|
307
|
+
const name = document.createElement('span');
|
|
308
|
+
name.className = 'hp-section-item__name';
|
|
309
|
+
name.textContent = labels[type] || type;
|
|
310
|
+
|
|
311
|
+
const typeSpan = document.createElement('span');
|
|
312
|
+
typeSpan.className = 'hp-section-item__type';
|
|
313
|
+
typeSpan.textContent = type;
|
|
314
|
+
|
|
315
|
+
info.appendChild(name);
|
|
316
|
+
info.appendChild(typeSpan);
|
|
317
|
+
|
|
318
|
+
const actions = document.createElement('div');
|
|
319
|
+
actions.className = 'hp-section-item__actions';
|
|
320
|
+
|
|
321
|
+
const btn = document.createElement('button');
|
|
322
|
+
btn.type = 'button';
|
|
323
|
+
btn.className = 'button button--small button--secondary';
|
|
324
|
+
btn.textContent = 'Remove';
|
|
325
|
+
btn.addEventListener('click', function() { removeFn(this); });
|
|
326
|
+
|
|
327
|
+
actions.appendChild(btn);
|
|
328
|
+
li.appendChild(info);
|
|
329
|
+
li.appendChild(actions);
|
|
330
|
+
|
|
331
|
+
return li;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function renderList(listEl, items, labels, removeFn, emptyText) {
|
|
335
|
+
listEl.textContent = '';
|
|
336
|
+
if (items.length === 0) {
|
|
337
|
+
const li = document.createElement('li');
|
|
338
|
+
li.className = 'hp-empty';
|
|
339
|
+
li.textContent = emptyText;
|
|
340
|
+
listEl.appendChild(li);
|
|
341
|
+
} else {
|
|
342
|
+
items.forEach(function(item) {
|
|
343
|
+
listEl.appendChild(createItemElement(item.type, labels, removeFn));
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
291
348
|
function addSection(id, label) {
|
|
292
349
|
sections.push({ type: id, config: {} });
|
|
293
350
|
updateSectionsList();
|
|
@@ -296,14 +353,17 @@
|
|
|
296
353
|
function removeSection(button) {
|
|
297
354
|
const item = button.closest('.hp-section-item');
|
|
298
355
|
const type = item.dataset.type;
|
|
299
|
-
sections = sections.filter(s
|
|
356
|
+
sections = sections.filter(function(s) { return s.type !== type; });
|
|
300
357
|
updateSectionsList();
|
|
301
358
|
}
|
|
302
359
|
|
|
303
360
|
function updateSectionsList() {
|
|
304
361
|
document.getElementById('sections-json').value = JSON.stringify(sections);
|
|
305
|
-
|
|
306
|
-
|
|
362
|
+
renderList(
|
|
363
|
+
document.getElementById('sections-list'),
|
|
364
|
+
sections, sectionLabels, removeSection,
|
|
365
|
+
'{{ __("homepage.sections.empty") }}'
|
|
366
|
+
);
|
|
307
367
|
}
|
|
308
368
|
|
|
309
369
|
function addWidget(id, label) {
|
|
@@ -314,36 +374,39 @@
|
|
|
314
374
|
function removeWidget(button) {
|
|
315
375
|
const item = button.closest('.hp-section-item');
|
|
316
376
|
const type = item.dataset.type;
|
|
317
|
-
sidebar = sidebar.filter(w
|
|
377
|
+
sidebar = sidebar.filter(function(w) { return w.type !== type; });
|
|
318
378
|
updateWidgetsList();
|
|
319
379
|
}
|
|
320
380
|
|
|
321
381
|
function updateWidgetsList() {
|
|
322
382
|
document.getElementById('sidebar-json').value = JSON.stringify(sidebar);
|
|
383
|
+
renderList(
|
|
384
|
+
document.getElementById('widgets-list'),
|
|
385
|
+
sidebar, widgetLabels, removeWidget,
|
|
386
|
+
'{{ __("homepage.sidebar.empty") }}'
|
|
387
|
+
);
|
|
323
388
|
}
|
|
324
389
|
|
|
325
390
|
// Layout selection
|
|
326
|
-
document.querySelectorAll('.hp-layout-option').forEach(option
|
|
327
|
-
option.addEventListener('click', ()
|
|
328
|
-
document.querySelectorAll('.hp-layout-option').forEach(o
|
|
391
|
+
document.querySelectorAll('.hp-layout-option').forEach(function(option) {
|
|
392
|
+
option.addEventListener('click', function() {
|
|
393
|
+
document.querySelectorAll('.hp-layout-option').forEach(function(o) { o.classList.remove('selected'); });
|
|
329
394
|
option.classList.add('selected');
|
|
330
395
|
});
|
|
331
396
|
});
|
|
332
397
|
|
|
333
|
-
// Hero checkboxes - convert to JSON
|
|
398
|
+
// Hero checkboxes - convert to JSON on submit
|
|
334
399
|
document.querySelector('form').addEventListener('submit', function(e) {
|
|
335
400
|
const heroEnabled = document.querySelector('input[name="hero[enabled]"]').checked;
|
|
336
401
|
const heroShowSocial = document.querySelector('input[name="hero[showSocial]"]').checked;
|
|
337
402
|
|
|
338
|
-
// Create hidden input with hero JSON
|
|
339
403
|
const heroInput = document.createElement('input');
|
|
340
404
|
heroInput.type = 'hidden';
|
|
341
405
|
heroInput.name = 'hero';
|
|
342
406
|
heroInput.value = JSON.stringify({ enabled: heroEnabled, showSocial: heroShowSocial });
|
|
343
407
|
this.appendChild(heroInput);
|
|
344
408
|
|
|
345
|
-
|
|
346
|
-
document.querySelectorAll('input[name^="hero["]').forEach(i => i.name = '');
|
|
409
|
+
document.querySelectorAll('input[name^="hero["]').forEach(function(i) { i.name = ''; });
|
|
347
410
|
});
|
|
348
411
|
</script>
|
|
349
412
|
{% endblock %}
|