@@ -203,6 +210,13 @@ class ServicesInfo {
}
});
});
+
+ document.querySelectorAll('.service-modal-edit').forEach((button) => {
+ button.addEventListener('click', () => {
+ window.Modal.hide();
+ this.widget.openServiceEditor(service);
+ });
+ });
}
}
diff --git a/monitorat/widgets/services/features/snapshot.js b/monitorat/widgets/services/features/snapshot.js
index 0089f26..96f7c4f 100644
--- a/monitorat/widgets/services/features/snapshot.js
+++ b/monitorat/widgets/services/features/snapshot.js
@@ -4,19 +4,12 @@
// Single-source is the trivial case: one source, no badges.
// Multi-source merges all services with source badges.
-const EDIT_ICON =
- '
';
-
class ServicesSnapshot {
constructor(widget) {
this.widget = widget;
this.info = widget.features.info;
}
- getEditIcon() {
- return EDIT_ICON;
- }
-
render() {
const cardsContainer = this.widget.container.querySelector('.service-grid');
if (!cardsContainer || !this.widget.servicesData) return;
@@ -107,11 +100,15 @@ class ServicesSnapshot {
const card = document.createElement('div');
const mode = this.widget.getDisplayMode();
const hasBadge = showBadge && service._source;
+ const canEdit = mode !== 'compact' && this.widget.canEditServices();
const baseClass =
mode === 'compact'
? 'service-card compact'
: 'service-card card status-card';
card.className = `${baseClass}${hasBadge ? ' has-badge' : ''}`;
+ if (canEdit) {
+ card.classList.add('editor-affordance-reveal-parent');
+ }
card.setAttribute('data-service-key', service._key);
card.setAttribute('data-service-source', service._source || '');
@@ -173,29 +170,19 @@ class ServicesSnapshot {
card.appendChild(iconContainer);
card.appendChild(info);
- const infoBtn = document.createElement('button');
- infoBtn.type = 'button';
- infoBtn.className = 'service-info-btn service-action-btn info-button';
- infoBtn.innerHTML = this.info.getInfoIcon();
- infoBtn.title = 'Service details';
- infoBtn.addEventListener('click', (event) => {
- event.stopPropagation();
- this.info.open(service);
- });
- card.appendChild(infoBtn);
-
- if (this.widget.canEditServices()) {
- const editBtn = document.createElement('button');
- editBtn.type = 'button';
- editBtn.className =
- 'service-edit-btn service-action-btn editor-edit-btn';
- editBtn.innerHTML = this.getEditIcon();
- editBtn.title = 'Edit service';
+
+ if (canEdit) {
+ const controls = window.monitorShared?.EditorControls;
+ const editAction = controls?.createCardOverflowButton({
+ title: 'Service details',
+ label: 'Service details',
+ });
+ const editBtn = editAction?.button || document.createElement('button');
editBtn.addEventListener('click', (event) => {
event.stopPropagation();
- this.widget.openServiceEditor(service);
+ this.info.open(service);
});
- card.appendChild(editBtn);
+ card.appendChild(editAction?.container || editBtn);
}
}
@@ -207,9 +194,8 @@ class ServicesSnapshot {
return;
}
if (
- event.target.closest('.service-info-btn') ||
event.target.closest('.service-status-dot') ||
- event.target.closest('.service-edit-btn')
+ event.target.closest('.editor-affordance-card-action')
) {
return;
}
@@ -317,13 +303,12 @@ class ServicesSnapshot {
});
}
- const hasBadge = card.classList.contains('has-badge');
- const isCompact = card.classList.contains('compact');
- const baseClass = isCompact
- ? 'service-card compact'
- : 'service-card card status-card';
const statusClass = this.widget.getStatusClass(overallStatus);
- card.className = `${baseClass}${hasBadge ? ' has-badge' : ''} ${statusClass}`;
+ const statusClasses = this.widget
+ .getStatusSeverity()
+ .map((status) => this.widget.getStatusClass(status));
+ card.classList.remove(...statusClasses);
+ card.classList.add(statusClass);
const statusTextElement = card.querySelector('.service-status');
if (statusTextElement) {
diff --git a/monitorat/widgets/services/style.css b/monitorat/widgets/services/style.css
index fc41b70..946c7e3 100644
--- a/monitorat/widgets/services/style.css
+++ b/monitorat/widgets/services/style.css
@@ -125,6 +125,7 @@
}
.service-info {
flex: 1;
+ padding-right: 28px;
}
.service-card.compact .service-info {
display: none;
@@ -178,42 +179,13 @@
.service-card.status-never .service-status-dot {
background: rgb(var(--status-unknown-rgb));
}
-.service-info-btn {
- position: absolute;
- right: 6px;
- bottom: 6px;
+.service-card .editor-affordance-card-action {
pointer-events: auto;
+ z-index: 2;
}
-.service-edit-btn {
- position: absolute;
- right: 38px;
- bottom: 6px;
+.service-card .editor-affordance-card-action .editor-affordance-btn {
pointer-events: auto;
}
-.service-action-btn {
- display: inline-flex;
- align-items: center;
- justify-content: center;
- width: 34px;
- height: 34px;
- padding: 7px;
- margin: 0;
- opacity: 0.58;
-}
-.service-action-btn:hover,
-.service-action-btn:focus-visible {
- opacity: 1;
- transform: none;
-}
-.service-action-btn svg {
- width: 100%;
- height: 100%;
- transition: transform 0.15s ease;
-}
-.service-action-btn:hover svg,
-.service-action-btn:focus-visible svg {
- transform: scale(1.14);
-}
.service-card.has-badge {
position: relative;
}
@@ -241,10 +213,18 @@
.url-picker-service {
display: flex;
align-items: center;
- gap: 12px;
+ justify-content: space-between;
+ gap: 16px;
margin-bottom: 16px;
}
+.url-picker-service-main {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ min-width: 0;
+}
+
.url-picker-title {
display: inline-flex;
align-items: center;
@@ -306,6 +286,7 @@
font-size: 1.1rem;
font-weight: 500;
color: var(--text-primary);
+ min-width: 0;
}
.url-picker-links {
@@ -436,3 +417,13 @@
width: 18px;
height: 18px;
}
+
+.service-modal-edit {
+ flex-shrink: 0;
+ color: var(--text-muted);
+}
+
+.service-modal-edit svg {
+ width: 18px;
+ height: 18px;
+}
diff --git a/monitorat/widgets/wiki/app.js b/monitorat/widgets/wiki/app.js
index 81ff9d9..8bd829d 100644
--- a/monitorat/widgets/wiki/app.js
+++ b/monitorat/widgets/wiki/app.js
@@ -313,23 +313,19 @@ class WikiWidget {
}
addEditButton() {
+ const headerElement = this.container.querySelector(
+ '[data-wiki-section-header="content"]',
+ );
const notesContainer = this.container.querySelector('.notes');
- const markdownBody = this.container.querySelector('.markdown-body');
- const targetElement = notesContainer || markdownBody;
-
- if (!targetElement) return;
-
- const editBtn = document.createElement('button');
- editBtn.className = 'editor-edit-btn hover-expand';
- editBtn.type = 'button';
- editBtn.title = 'Edit';
- editBtn.setAttribute('aria-label', 'Edit document');
- editBtn.innerHTML = `
-
- `;
+ if (!notesContainer) return;
+
+ const controls = window.monitorShared?.EditorControls;
+ const editBtn =
+ controls?.createEditButton({
+ title: 'Edit',
+ label: 'Edit document',
+ iconName: 'edit-doc',
+ }) || document.createElement('button');
editBtn.addEventListener('click', (event) => {
event.stopPropagation();
@@ -338,13 +334,15 @@ class WikiWidget {
}
});
- if (notesContainer) {
- notesContainer.insertBefore(editBtn, notesContainer.firstChild);
- } else if (markdownBody) {
- markdownBody.insertBefore(editBtn, markdownBody.firstChild);
- } else {
- targetElement.appendChild(editBtn);
+ if (headerElement?.textContent?.trim()) {
+ headerElement.classList.add('editor-affordance-reveal-parent');
+ headerElement.appendChild(editBtn);
+ return;
}
+
+ notesContainer.classList.add('editor-affordance-reveal-parent');
+ editBtn.classList.add('editor-affordance-corner', 'wiki-edit-floating-btn');
+ notesContainer.appendChild(editBtn);
}
}
diff --git a/monitorat/widgets/wiki/style.css b/monitorat/widgets/wiki/style.css
index c27cfe2..8d9980b 100644
--- a/monitorat/widgets/wiki/style.css
+++ b/monitorat/widgets/wiki/style.css
@@ -9,11 +9,20 @@
background: transparent;
padding: 16px 18px;
}
-.notes .editor-edit-btn {
- float: inline-end;
- margin-block-start: 10px;
- margin-inline-end: 10px;
- margin-inline-start: 0;
+.wiki .feature-header[data-wiki-section-header="content"] {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+.wiki
+ .feature-header[data-wiki-section-header="content"]
+ > .editor-affordance-btn {
+ margin-inline-start: auto;
+}
+.notes .wiki-edit-floating-btn {
+ top: 10px;
+ right: 10px;
+ z-index: 2;
}
.notes[data-mode="seamless"],
.notes[data-mode="rail"] {
@@ -30,11 +39,10 @@
border-inline-start: 2px solid var(--border-muted);
padding-inline-start: 16px;
}
-.notes[data-mode="seamless"] .editor-edit-btn,
-.notes[data-mode="rail"] .editor-edit-btn {
- margin-block: 0 8px;
- margin-inline-start: 8px;
- margin-inline-end: 0;
+.notes[data-mode="seamless"] .wiki-edit-floating-btn,
+.notes[data-mode="rail"] .wiki-edit-floating-btn {
+ top: 0;
+ right: 0;
}
.notes .markdown-body details {
margin-bottom: 1em;