Files
pocketbase/ui/src/base/ruleField.js
2026-04-18 16:50:39 +03:00

166 lines
4.8 KiB
JavaScript

window.app = window.app || {};
window.app.components = window.app.components || {};
/**
* API rule input element.
*
* @example
* ```js
* app.components.ruleField({
* name: "listRule",
* autocomplete: (word) => {
* return app.utils.collectionAutocompleteKeys(someCollection, word);
* },
* value: () => someCollection.listRule,
* oninput: (newVal) => someCollection.listRule = newVal,
* })
* ```
*
* @param {Object} [propsArg]
* @return {Element}
*/
window.app.components.ruleField = function(propsArg = {}) {
const uniqueId = "rule_" + app.utils.randomString();
const props = store({
rid: undefined,
id: undefined,
hidden: undefined,
inert: undefined,
className: "",
required: false,
disabled: false,
name: undefined,
label: undefined,
help: undefined,
value: null,
nullable: true,
placeholder: "Leave empty to grant everyone access...",
autocomplete: (word) => [],
oninput: (newVal) => {},
onmount: (el) => {},
onunmount: (el) => {},
// ---
get isLocked() {
return props.value == null;
},
});
const watchers = app.utils.extendStore(props, propsArg, "isLocked");
let ruleField;
let _prevValue = "";
function updateValue(newValue) {
props.value = newValue;
props.oninput?.(newValue);
ruleField?.dispatchEvent(new CustomEvent("change", { detail: newValue }));
}
function lock() {
if (props.value === null) {
return;
}
_prevValue = props.value;
updateValue(null);
}
function unlock() {
if (_prevValue != null) {
updateValue(_prevValue);
} else {
updateValue("");
}
setTimeout(() => {
document.getElementById(uniqueId)?.focus();
}, 0);
}
ruleField = t.div(
{
rid: props.rid,
id: () => props.id,
hidden: () => props.hidden,
inert: () => props.inert,
"html-name": () => props.name, // used for the error reset
className: () =>
[
"field",
"rule-field",
props.required ? "required" : null,
props.value === null ? "locked" : null,
props.disabled ? "disabled" : null,
].filter(Boolean).join(" "),
onmount: (el) => {
props.onmount?.(el);
},
onunmount: (el) => {
props.onunmount?.(el);
watchers.forEach((w) => w?.unwatch());
},
},
t.label(
{ htmlFor: uniqueId },
(el) => {
if (!props.label) {
return t.span({ className: "txt" }, "Rule");
}
if (typeof props.label == "function") {
return props.label(el);
}
if (typeof props.label == "string") {
return t.span({ className: "txt" }, props.label);
}
return props.label;
},
t.span({ hidden: () => !props.isLocked, className: "txt superusers-label" }, "(Superusers only)"),
),
(el) => {
if (props.isLocked) {
return t.button(
{
type: "button",
className: "unlock-overlay",
disabled: () => props.disabled,
onclick: unlock,
},
t.span({ className: "txt" }, "Unlock and set custom rule"),
t.i({ className: "ri-lock-unlock-line", ariaHidden: true }),
);
}
return [
app.components.codeEditor({
id: uniqueId,
language: "pbrule",
required: () => props.required,
disabled: () => props.disabled,
value: () => props.value,
oninput: updateValue,
placeholder: () => props.placeholder,
autocomplete: props.autocomplete,
autocompleteContainer: el,
}),
t.button(
{
hidden: () => !props.nullable,
type: "button",
className: "superuser-toggle",
disabled: () => props.disabled,
onclick: lock,
},
t.i({ className: "ri-lock-line", ariaHidden: true }),
t.span({ className: "txt" }, "Set superusers only"),
),
];
},
);
return ruleField;
};