added error marker for each collection tab and fixed the styles of the raw errors tooltip

This commit is contained in:
Gani Georgiev
2026-05-14 09:15:05 +03:00
parent b061673d9a
commit 820b9afe98
13 changed files with 103 additions and 25 deletions
+1 -1
View File
@@ -12,4 +12,4 @@ PB_DOCS_URL = "https://pocketbase.io/docs"
PB_JS_SDK_URL = "https://github.com/pocketbase/js-sdk"
PB_DART_SDK_URL = "https://github.com/pocketbase/dart-sdk"
PB_RELEASES = "https://github.com/pocketbase/pocketbase/releases"
PB_VERSION = "v0.38.1-dev"
PB_VERSION = "v0.38.1"
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -13,9 +13,9 @@
<!-- prism -->
<script src="./libs/prism/prism.js" data-manual></script>
<script type="module" crossorigin src="./assets/index-C5XHp9-e.js"></script>
<script type="module" crossorigin src="./assets/index-Dko-neZT.js"></script>
<link rel="modulepreload" crossorigin href="./assets/pocketbase.es-B_4DUNUU.js">
<link rel="stylesheet" crossorigin href="./assets/index-DR1PnFnr.css">
<link rel="stylesheet" crossorigin href="./assets/index-DRWtZ-mO.css">
</head>
<body>
</body>
+6 -5
View File
@@ -7,9 +7,10 @@ const tooltip = t.div({
});
document.body.appendChild(tooltip);
function updateTooltipPosition(node, position) {
function updateTooltipPosition(node, position, styleClass) {
let nodeRect = node.getBoundingClientRect();
tooltip.setAttribute("data-style", styleClass || "");
tooltip.setAttribute("data-position", position);
// reset tooltip position
@@ -76,7 +77,7 @@ function hideTooltip() {
tooltip.hidePopover();
}
function showTooltip(node, text, position) {
function showTooltip(node, text, position, styleClass) {
if (!node || !text) {
hideTooltip();
return;
@@ -84,14 +85,14 @@ function showTooltip(node, text, position) {
tooltip.showPopover();
tooltip.textContent = text;
updateTooltipPosition(node, position);
updateTooltipPosition(node, position, styleClass);
}
document.body.addEventListener("mouseleave", () => {
hideTooltip();
});
function tooltipAction(textOrFunc, position = "top") {
function tooltipAction(textOrFunc, position = "top", styleClass = "") {
return (el) => {
if (!el._tooltipText) {
el._tooltipText = store({
@@ -105,7 +106,7 @@ function tooltipAction(textOrFunc, position = "top") {
tooltipTextWatcher = watch(
() => el._tooltipText.value,
async (result) => {
showTooltip(el, result, position);
showTooltip(el, result, position, styleClass);
},
);
}
@@ -342,6 +342,9 @@ window.app.modals.openCollectionChangesConfirmation = async function(
),
yesCallback,
noCallback,
{ className: "collection-changes-confirm-modal" },
{
className: "collection-changes-confirm-modal",
yesButton: "Yes, save changes",
},
);
};
+64 -7
View File
@@ -63,6 +63,7 @@ function collectionUpsertModal(rawCollection, modalSettings) {
originalCollection: {},
collection: {},
selectedTab: "",
errorTabs: {},
get activeTab() {
if (!app.collectionTypes[data.collection.type]?.tabs) {
return data.selectedTab;
@@ -211,6 +212,9 @@ function collectionUpsertModal(rawCollection, modalSettings) {
function resetForm() {
data.collection = JSON.parse(JSON.stringify(data.originalCollection));
// reset all errors
app.store.errors = null;
}
async function duplicate() {
@@ -345,6 +349,30 @@ function collectionUpsertModal(rawCollection, modalSettings) {
}, 150);
},
),
// check for tab errors
watch(
() => JSON.stringify(app.store.errors),
async (errors) => {
if (!errors) {
data.errorTabs = {};
return;
}
// ui tick
await new Promise((r) => setTimeout(r, 0));
data.errorTabs = {}; // reset after the tick to minimize flickering
const errorElems = modal?.querySelectorAll(".modal-content [data-tab] .error");
for (let el of errorElems) {
const tabName = el.closest("[data-tab]")?.dataset?.tab;
if (tabName) {
data.errorTabs[tabName] = true;
}
}
},
),
];
},
onunmount: (el) => {
@@ -546,24 +574,35 @@ function collectionUpsertModal(rawCollection, modalSettings) {
t.nav(
{ className: "tabs-header equal-width" },
() => {
const tabItems = [];
const tabHeaderItems = [];
const tabs = app.collectionTypes[data.collection.type]?.tabs || {};
for (let tabName in tabs) {
tabItems.push(
tabHeaderItems.push(
t.button(
{
type: "button",
"html-data-tab": tabName,
disabled: () => data.isSaving,
className: () => `tab-item ${data.activeTab == tabName ? "active" : ""}`,
onclick: () => changeTab(tabName),
},
t.span({ className: "txt" }, tabName),
() => {
if (!data.errorTabs[tabName]) {
return;
}
return t.i({
className: "ri-error-warning-fill txt-danger txt-base",
ariaDescription: app.attrs.tooltip("Has errors"),
});
},
),
);
}
return tabItems;
return tabHeaderItems;
},
),
),
@@ -571,7 +610,25 @@ function collectionUpsertModal(rawCollection, modalSettings) {
),
t.div(
{ className: "modal-content" },
() => app.collectionTypes[data.collection.type]?.tabs?.[data.activeTab]?.(data),
() => {
const tabContentItems = [];
const tabs = app.collectionTypes[data.collection.type]?.tabs || {};
for (let tabName in tabs) {
tabContentItems.push(
t.div(
{
"html-data-tab": tabName,
hidden: () => data.activeTab != tabName,
className: "tab-content-wrapper block",
},
() => app.collectionTypes[data.collection.type]?.tabs?.[tabName]?.(data),
),
);
}
return tabContentItems;
},
),
t.footer(
{ className: "modal-footer" },
@@ -585,14 +642,14 @@ function collectionUpsertModal(rawCollection, modalSettings) {
t.span({ className: "txt" }, "Close"),
),
() => {
const rawErrors = JSON.stringify(app.store.errors);
const rawErrors = JSON.stringify(app.store.errors, null, 2);
if (rawErrors == "" || rawErrors == "null" || rawErrors == "{}" || rawErrors == "[]") {
return;
}
return t.i({
className: "ri-alert-line txt-danger",
ariaDescription: app.attrs.tooltip(() => "Raw error:\n" + rawErrors),
className: "ri-error-warning-line txt-danger",
ariaDescription: app.attrs.tooltip(() => "Raw errors:\n" + rawErrors, "top", "code"),
});
},
t.div(
+4
View File
@@ -411,6 +411,10 @@ hr {
}
/* txt helpers */
.txt-base {
line-height: var(--lineHeight) !important;
font-size: var(--fontSize) !important;
}
.txt-sm {
line-height: var(--smLineHeight) !important;
font-size: var(--smFontSize) !important;
+3
View File
@@ -65,6 +65,9 @@
i {
display: inline-block;
}
.label {
vertical-align: baseline;
}
}
.rule-content {
margin: 5px 0;
+8
View File
@@ -3,6 +3,7 @@
z-index: 99999;
max-width: 300px;
min-width: 30px;
max-height: 90%;
text-align: center;
white-space: pre-line;
word-break: break-word;
@@ -13,6 +14,7 @@
font-size: var(--smFontSize);
line-height: var(--smLineHeight);
font-family: var(--baseFontFamily);
scrollbar-width: thin;
margin: 3px;
padding: 3px 5px;
pointer-events: none;
@@ -27,4 +29,10 @@
opacity: 0;
}
}
&[data-style="code"] {
text-align: left;
white-space: pre-wrap;
max-width: 350px;
font-family: var(--monospaceFontFamily);
}
}
+1 -1
View File
@@ -88,7 +88,7 @@
--modalOverlayColor: rgba(70, 90, 115, 0.25);
--tooltipTxtColor: #fff;
--tooltipSurfaceColor: rgba(34, 36, 36, 0.9);
--tooltipSurfaceColor: rgba(37, 39, 45, 0.9);
--boxShadow: 0px 8px 5px -5px rgba(34, 36, 36, 0.1);
--leftBoxShadow: -1px 0px 5px 0 rgba(34, 36, 36, 0.1);