Files
lcbp3.np-dms.work/n8n-cache/n8n/public/assets/ResourcesListLayout-CHxMfd0o.js
2025-09-21 20:29:15 +07:00

1284 lines
50 KiB
JavaScript
Executable File

import { d as defineComponent, r as ref, x as computed, h as createElementBlock, g as openBlock, i as createVNode, j as createBaseVNode, w as withCtx, F as Fragment, A as renderList, ap as normalizeStyle, n as normalizeClass, t as toDisplayString, X as renderSlot, e as createBlock, c3 as resolveDynamicComponent, l as unref, f as createCommentVNode, ft as _sfc_main$6, e2 as _sfc_main$7, k as createTextVNode, D as useI18n, e1 as N8nSelect, K as mergeProps, _ as _export_sfc, a7 as watch, b1 as onBeforeMount, o as onMounted, Y as nextTick, au as useProjectsStore, a8 as resolveComponent, fp as _sfc_main$8, aE as EnterpriseEditionFeature, c as useI18n$1, d_ as N8nInputLabel, fu as ProjectSharing, C as N8nLink, aK as N8nBadge, q as N8nButton, a_ as useLocalStorage, fv as LOCAL_STORAGE_WORKFLOW_LIST_PREFERENCES_KEY, fw as isBaseTextKey, a2 as useRoute, b as useRouter, P as useDebounce, u as useUsersStore, fx as useSlots, fy as isSharedResource, fz as isResourceSortableByDate, W as onBeforeUnmount, dD as N8nLoading, dR as N8nActionBox, p as N8nText, b2 as withDirectives, cT as N8nInput, N as N8nIcon, c1 as normalizeProps, c2 as guardReactiveProps, cK as InfoTip, b3 as vShow, al as useTelemetry } from "./index-CeNA_ukL.js";
import { N as N8nTableBase } from "./TableBase-DmNxoh-V.js";
import { u as useProjectPages } from "./useProjectPages-xv6Eq2Y5.js";
function getValueByPath(object, path) {
return path.split(".").reduce((acc, part) => {
return acc?.[part];
}, object);
}
const _hoisted_1$1 = { key: 1 };
const _hoisted_2$1 = { class: "pagination" };
const _hoisted_3$1 = { class: "pageSizeSelector" };
const ALL_ROWS = -1;
const _sfc_main$5 = /* @__PURE__ */ defineComponent({
...{ name: "N8nDatatable" },
__name: "Datatable",
props: {
columns: {},
rows: {},
currentPage: { default: 1 },
pagination: { type: Boolean, default: true },
rowsPerPage: { default: 10 }
},
emits: ["update:currentPage", "update:rowsPerPage"],
setup(__props, { emit: __emit }) {
const props = __props;
const emit = __emit;
const { t } = useI18n();
const rowsPerPageOptions = ref([1, 10, 25, 50, 100]);
const totalPages = computed(() => {
return Math.ceil(props.rows.length / props.rowsPerPage);
});
const totalRows = computed(() => {
return props.rows.length;
});
const visibleRows = computed(() => {
if (props.rowsPerPage === ALL_ROWS) return props.rows;
const start = (props.currentPage - 1) * props.rowsPerPage;
const end = start + props.rowsPerPage;
return props.rows.slice(start, end);
});
function onUpdateCurrentPage(value) {
emit("update:currentPage", value);
}
function onRowsPerPageChange(value) {
emit("update:rowsPerPage", value);
if (value === ALL_ROWS) {
onUpdateCurrentPage(1);
return;
}
const maxPage = Math.ceil(totalRows.value / value);
if (maxPage < props.currentPage) {
onUpdateCurrentPage(maxPage);
}
}
function getTdValue(row, column) {
return getValueByPath(row, column.path);
}
function getThStyle(column) {
return {
...column.width ? { width: column.width } : {}
};
}
return (_ctx, _cache) => {
return openBlock(), createElementBlock("div", mergeProps({ class: "datatable datatableWrapper" }, _ctx.$attrs), [
createVNode(unref(N8nTableBase), null, {
default: withCtx(() => [
createBaseVNode("thead", null, [
createBaseVNode("tr", null, [
(openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.columns, (column) => {
return openBlock(), createElementBlock("th", {
key: column.id,
class: normalizeClass(column.classes),
style: normalizeStyle(getThStyle(column))
}, toDisplayString(column.label), 7);
}), 128))
])
]),
createBaseVNode("tbody", null, [
(openBlock(true), createElementBlock(Fragment, null, renderList(visibleRows.value, (row) => {
return renderSlot(_ctx.$slots, "row", {
columns: _ctx.columns,
row,
getTdValue
}, () => [
(openBlock(), createElementBlock("tr", {
key: row.id
}, [
(openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.columns, (column) => {
return openBlock(), createElementBlock("td", {
key: column.id,
class: normalizeClass(column.classes)
}, [
column.render ? (openBlock(), createBlock(resolveDynamicComponent(column.render), {
key: 0,
row,
column
}, null, 8, ["row", "column"])) : (openBlock(), createElementBlock("span", _hoisted_1$1, toDisplayString(getTdValue(row, column)), 1))
], 2);
}), 128))
]))
], true);
}), 256))
])
]),
_: 3
}),
createBaseVNode("div", _hoisted_2$1, [
totalPages.value > 1 ? (openBlock(), createBlock(unref(_sfc_main$6), {
key: 0,
background: "",
"pager-count": 5,
"page-size": _ctx.rowsPerPage,
layout: "prev, pager, next",
total: totalRows.value,
"current-page": _ctx.currentPage,
"onUpdate:currentPage": onUpdateCurrentPage
}, null, 8, ["page-size", "total", "current-page"])) : createCommentVNode("", true),
createBaseVNode("div", _hoisted_3$1, [
createVNode(unref(N8nSelect), {
size: "mini",
"model-value": _ctx.rowsPerPage,
teleported: "",
"onUpdate:modelValue": onRowsPerPageChange
}, {
prepend: withCtx(() => [
createTextVNode(toDisplayString(unref(t)("datatable.pageSize")), 1)
]),
default: withCtx(() => [
(openBlock(true), createElementBlock(Fragment, null, renderList(rowsPerPageOptions.value, (size) => {
return openBlock(), createBlock(unref(_sfc_main$7), {
key: size,
label: `${size}`,
value: size
}, null, 8, ["label", "value"]);
}), 128)),
createVNode(unref(_sfc_main$7), {
label: `All`,
value: ALL_ROWS
})
]),
_: 1
}, 8, ["model-value"])
])
])
], 16);
};
}
});
const N8nDatatable = /* @__PURE__ */ _export_sfc(_sfc_main$5, [["__scopeId", "data-v-eeb5972f"]]);
const _sfc_main$4 = /* @__PURE__ */ defineComponent({
__name: "RecycleScroller",
props: {
itemSize: {},
items: {},
itemKey: {},
offset: { default: 2 }
},
setup(__props) {
const props = __props;
const wrapperRef = ref(null);
const scrollerRef = ref(null);
const itemsRef = ref(null);
const itemRefs = ref({});
const scrollTop = ref(0);
const wrapperHeight = ref(0);
const windowHeight = ref(0);
const itemSizeCache = ref({});
const itemPositionCache = computed(() => {
return props.items.reduce(
(acc, item, index) => {
const key = item[props.itemKey];
const prevItem = props.items[index - 1];
const prevItemPosition = prevItem ? acc[prevItem[props.itemKey]] : 0;
const prevItemSize = prevItem ? itemSizeCache.value[prevItem[props.itemKey]] : 0;
acc[key] = prevItemPosition + prevItemSize;
return acc;
},
{}
);
});
const startIndex = computed(() => {
const foundIndex = props.items.findIndex((item) => {
const itemPosition = itemPositionCache.value[item[props.itemKey]];
return itemPosition >= scrollTop.value;
}) - 1;
const index = foundIndex - props.offset;
return index < 0 ? 0 : index;
});
const endIndex = computed(() => {
const foundIndex = props.items.findIndex((item) => {
const itemPosition = itemPositionCache.value[item[props.itemKey]];
const itemSize = itemSizeCache.value[item[props.itemKey]];
return itemPosition + itemSize >= scrollTop.value + wrapperHeight.value;
});
const index = foundIndex + props.offset;
return foundIndex === -1 ? props.items.length - 1 : index;
});
const visibleItems = computed(() => {
return props.items.slice(startIndex.value, endIndex.value + 1);
});
watch(
() => visibleItems.value,
(currentValue, previousValue) => {
const difference = currentValue.filter(
(currentItem) => !previousValue.find(
(previousItem) => previousItem[props.itemKey] === currentItem[props.itemKey]
)
);
if (difference.length > 0) {
updateItemSizeCache(difference);
}
}
);
const scrollerHeight = computed(() => {
const lastItem = props.items[props.items.length - 1];
const lastItemPosition = lastItem ? itemPositionCache.value[lastItem[props.itemKey]] : 0;
const lastItemSize = lastItem ? itemSizeCache.value[lastItem[props.itemKey]] : props.itemSize;
return lastItemPosition + lastItemSize;
});
const scrollerStyles = computed(() => ({
height: `${scrollerHeight.value}px`
}));
const itemsStyles = computed(() => {
const offset = itemPositionCache.value[props.items[startIndex.value][props.itemKey]];
return {
transform: `translateY(${offset}px)`
};
});
onBeforeMount(() => {
initializeItemSizeCache();
});
onMounted(() => {
if (wrapperRef.value) {
wrapperRef.value.addEventListener("scroll", onScroll);
updateItemSizeCache(visibleItems.value);
}
window.addEventListener("resize", onWindowResize);
onWindowResize();
});
function initializeItemSizeCache() {
props.items.forEach((item) => {
itemSizeCache.value = {
...itemSizeCache.value,
[item[props.itemKey]]: props.itemSize
};
});
}
function updateItemSizeCache(items) {
for (const item of items) {
onUpdateItemSize(item);
}
}
function onUpdateItemSize(item) {
void nextTick(() => {
const itemId = item[props.itemKey];
const itemRef = itemRefs.value[itemId];
const previousSize = itemSizeCache.value[itemId];
const size = itemRef ? itemRef.offsetHeight : props.itemSize;
const difference = size - previousSize;
itemSizeCache.value = {
...itemSizeCache.value,
[item[props.itemKey]]: size
};
if (wrapperRef.value && scrollTop.value) {
wrapperRef.value.scrollTop = wrapperRef.value.scrollTop + difference;
scrollTop.value = wrapperRef.value.scrollTop;
}
});
}
function onWindowResize() {
if (wrapperRef.value) {
wrapperHeight.value = wrapperRef.value.offsetHeight;
void nextTick(() => {
updateItemSizeCache(visibleItems.value);
});
}
windowHeight.value = window.innerHeight;
}
function onScroll() {
if (!wrapperRef.value) {
return;
}
scrollTop.value = wrapperRef.value.scrollTop;
}
return (_ctx, _cache) => {
return openBlock(), createElementBlock("div", {
ref_key: "wrapperRef",
ref: wrapperRef,
class: "recycle-scroller-wrapper"
}, [
createBaseVNode("div", {
ref_key: "scrollerRef",
ref: scrollerRef,
class: "recycle-scroller",
style: normalizeStyle(scrollerStyles.value)
}, [
createBaseVNode("div", {
ref_key: "itemsRef",
ref: itemsRef,
class: "recycle-scroller-items-wrapper",
style: normalizeStyle(itemsStyles.value)
}, [
(openBlock(true), createElementBlock(Fragment, null, renderList(visibleItems.value, (item) => {
return openBlock(), createElementBlock("div", {
key: item[_ctx.itemKey],
ref_for: true,
ref: (element) => itemRefs.value[`${item[_ctx.itemKey]}`] = element,
class: "recycle-scroller-item"
}, [
renderSlot(_ctx.$slots, "default", {
item,
updateItemSize: onUpdateItemSize
})
]);
}), 128))
], 4)
], 4)
], 512);
};
}
});
const wrapper$1 = "_wrapper_1x8n3_123";
const content = "_content_1x8n3_139";
const style0$3 = {
wrapper: wrapper$1,
content
};
const _sfc_main$3 = {};
function _sfc_render(_ctx, _cache) {
return openBlock(), createElementBlock("div", {
class: normalizeClass(_ctx.$style.wrapper)
}, [
renderSlot(_ctx.$slots, "header"),
createBaseVNode("main", {
class: normalizeClass(_ctx.$style.content)
}, [
renderSlot(_ctx.$slots, "default")
], 2)
], 2);
}
const cssModules$3 = {
"$style": style0$3
};
const PageViewLayout = /* @__PURE__ */ _export_sfc(_sfc_main$3, [["render", _sfc_render], ["__cssModules", cssModules$3]]);
const _sfc_main$2 = /* @__PURE__ */ defineComponent({
__name: "PageViewLayoutList",
props: {
overflow: { type: Boolean }
},
setup(__props) {
return (_ctx, _cache) => {
return openBlock(), createElementBlock("div", {
class: normalizeClass({ [_ctx.$style.wrapper]: true, [_ctx.$style.overflow]: _ctx.overflow })
}, [
createBaseVNode("div", {
class: normalizeClass(_ctx.$style.list)
}, [
_ctx.$slots.header ? (openBlock(), createElementBlock("div", {
key: 0,
class: normalizeClass(_ctx.$style.header)
}, [
renderSlot(_ctx.$slots, "header")
], 2)) : createCommentVNode("", true),
createBaseVNode("div", {
class: normalizeClass(_ctx.$style.body)
}, [
renderSlot(_ctx.$slots, "default")
], 2)
], 2)
], 2);
};
}
});
const wrapper = "_wrapper_hn9dc_123";
const overflow = "_overflow_hn9dc_128";
const list = "_list_hn9dc_128";
const body = "_body_hn9dc_128";
const style0$2 = {
wrapper,
overflow,
list,
body
};
const cssModules$2 = {
"$style": style0$2
};
const PageViewLayoutList = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["__cssModules", cssModules$2]]);
const _sfc_main$1 = /* @__PURE__ */ defineComponent({
__name: "ResourceFiltersDropdown",
props: {
modelValue: { default: () => ({}) },
keys: { default: () => [] },
shareable: { type: Boolean, default: true },
reset: { type: Function, default: () => {
} },
justIcon: { type: Boolean, default: false }
},
emits: ["update:modelValue", "update:filtersLength"],
setup(__props, { emit: __emit }) {
const props = __props;
const emit = __emit;
const projectsStore = useProjectsStore();
const i18n = useI18n$1();
const selectedProject = computed({
get: () => {
return projectsStore.availableProjects.find(
(project) => project.id === props.modelValue.homeProject
) ?? null;
},
set: (value) => setKeyValue("homeProject", value?.id ?? "")
});
const filtersLength = computed(() => {
let length = 0;
props.keys.forEach((key) => {
if (key === "search") {
return;
}
const value = props.modelValue[key];
if (value === true) {
length += 1;
}
if (Array.isArray(value) && value.length) {
length += 1;
}
if (typeof value === "string" && value !== "") {
length += 1;
}
});
return length;
});
const hasFilters = computed(() => filtersLength.value > 0);
const setKeyValue = (key, value) => {
const filters2 = {
...props.modelValue,
[key]: value
};
emit("update:modelValue", filters2);
};
const resetFilters = () => {
if (props.reset) {
props.reset();
} else {
const filters2 = { ...props.modelValue };
props.keys.forEach((key) => {
filters2[key] = Array.isArray(props.modelValue[key]) ? [] : "";
});
emit("update:modelValue", filters2);
}
selectedProject.value = null;
};
watch(filtersLength, (value) => {
emit("update:filtersLength", value);
});
onBeforeMount(async () => {
await projectsStore.getAvailableProjects();
});
return (_ctx, _cache) => {
const _component_n8n_badge = N8nBadge;
const _component_n8n_button = N8nButton;
const _component_n8n_input_label = N8nInputLabel;
const _component_enterprise_edition = resolveComponent("enterprise-edition");
const _component_n8n_link = N8nLink;
const _component_n8n_popover = _sfc_main$8;
return openBlock(), createBlock(_component_n8n_popover, {
trigger: "click",
width: "304",
size: "large"
}, {
reference: withCtx(() => [
createVNode(_component_n8n_button, {
icon: "funnel",
type: "tertiary",
size: "small",
active: hasFilters.value,
class: normalizeClass({
[_ctx.$style["filter-button"]]: true,
[_ctx.$style["no-label"]]: _ctx.justIcon && filtersLength.value === 0
}),
"data-test-id": "resources-list-filters-trigger"
}, {
default: withCtx(() => [
filtersLength.value > 0 ? (openBlock(), createBlock(_component_n8n_badge, {
key: 0,
class: normalizeClass(_ctx.$style["filter-button-count"]),
"data-test-id": "resources-list-filters-count",
theme: "primary"
}, {
default: withCtx(() => [
createTextVNode(toDisplayString(filtersLength.value), 1)
]),
_: 1
}, 8, ["class"])) : createCommentVNode("", true),
!_ctx.justIcon ? (openBlock(), createElementBlock("span", {
key: 1,
class: normalizeClass(_ctx.$style["filter-button-text"])
}, toDisplayString(unref(i18n).baseText("forms.resourceFiltersDropdown.filters")), 3)) : createCommentVNode("", true)
]),
_: 1
}, 8, ["active", "class"])
]),
default: withCtx(() => [
createBaseVNode("div", {
class: normalizeClass(_ctx.$style["filters-dropdown"]),
"data-test-id": "resources-list-filters-dropdown"
}, [
renderSlot(_ctx.$slots, "default", {
filters: _ctx.modelValue,
setKeyValue
}),
_ctx.shareable && unref(projectsStore).isProjectHome ? (openBlock(), createBlock(_component_enterprise_edition, {
key: 0,
features: [unref(EnterpriseEditionFeature).Sharing]
}, {
default: withCtx(() => [
createVNode(_component_n8n_input_label, {
label: unref(i18n).baseText("forms.resourceFiltersDropdown.owner"),
bold: false,
size: "small",
color: "text-base",
class: "mb-3xs"
}, null, 8, ["label"]),
createVNode(ProjectSharing, {
modelValue: selectedProject.value,
"onUpdate:modelValue": [
_cache[0] || (_cache[0] = ($event) => selectedProject.value = $event),
_cache[1] || (_cache[1] = ($event) => setKeyValue("homeProject", $event.id))
],
projects: unref(projectsStore).availableProjects,
placeholder: unref(i18n).baseText("forms.resourceFiltersDropdown.owner.placeholder"),
"empty-options-text": unref(i18n).baseText("projects.sharing.noMatchingProjects")
}, null, 8, ["modelValue", "projects", "placeholder", "empty-options-text"])
]),
_: 1
}, 8, ["features"])) : createCommentVNode("", true),
hasFilters.value ? (openBlock(), createElementBlock("div", {
key: 1,
class: normalizeClass([_ctx.$style["filters-dropdown-footer"], "mt-s"])
}, [
createVNode(_component_n8n_link, { onClick: resetFilters }, {
default: withCtx(() => [
createTextVNode(toDisplayString(unref(i18n).baseText("forms.resourceFiltersDropdown.reset")), 1)
]),
_: 1
})
], 2)) : createCommentVNode("", true)
], 2)
]),
_: 3
});
};
}
});
const style0$1 = {
"filter-button": "_filter-button_1llux_123",
"no-label": "_no-label_1llux_127",
"filter-button-count": "_filter-button-count_1llux_133",
"filter-button-text": "_filter-button-text_1llux_142",
"filters-dropdown": "_filters-dropdown_1llux_150",
"filters-dropdown-footer": "_filters-dropdown-footer_1llux_154"
};
const cssModules$1 = {
"$style": style0$1
};
const ResourceFiltersDropdown = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["__cssModules", cssModules$1]]);
function useN8nLocalStorage() {
const projectPages = useProjectPages();
const getProjectKey = (projectId) => {
return projectPages.isOverviewSubPage ? "home" : projectId;
};
const saveProjectPreferencesToLocalStorage = (projectId, tabKey, preferences) => {
const projectKey = getProjectKey(projectId);
if (!projectKey) {
return;
}
const localStorage = useLocalStorage(
LOCAL_STORAGE_WORKFLOW_LIST_PREFERENCES_KEY,
{}
);
if (!localStorage.value[projectKey]) {
localStorage.value[projectKey] = {};
}
localStorage.value[projectKey][tabKey] = {
...localStorage.value[projectKey][tabKey],
...preferences
};
};
const loadProjectPreferencesFromLocalStorage = (projectId, tabKey) => {
const projectKey = getProjectKey(projectId);
if (!projectKey) {
return {};
}
const localStorage = useLocalStorage(
LOCAL_STORAGE_WORKFLOW_LIST_PREFERENCES_KEY,
{}
);
const projectPreferences = localStorage.value[projectKey]?.[tabKey] || {};
return projectPreferences;
};
return {
saveProjectPreferencesToLocalStorage,
loadProjectPreferencesFromLocalStorage,
getProjectKey
};
}
function useResourcesListI18n(resourceKey) {
const i18n = useI18n$1();
const getResourceText = (keySuffix, fallbackKeySuffix, interpolate) => {
const specificKey = `${resourceKey}.${keySuffix}`;
const genericKey = `resources.${keySuffix}`;
const fallbackKey = fallbackKeySuffix ? `resources.${fallbackKeySuffix}` : void 0;
if (isBaseTextKey(specificKey)) {
return i18n.baseText(specificKey, { interpolate });
}
if (isBaseTextKey(genericKey)) {
return i18n.baseText(genericKey, { interpolate });
}
if (fallbackKey && isBaseTextKey(fallbackKey)) {
return i18n.baseText(fallbackKey, { interpolate });
}
return keySuffix.split(".").pop()?.replace(/([A-Z])/g, " $1").trim() || keySuffix;
};
return {
getResourceText
};
}
const _hoisted_1 = {
key: 0,
class: "resource-list-loading"
};
const _hoisted_2 = { key: 0 };
const _hoisted_3 = {
key: 0,
class: "mt-xs",
"data-test-id": "resources-list-filters-applied-info"
};
const _hoisted_4 = {
key: 0,
class: "resource-list-loading resource-list-loading-instant"
};
const _sfc_main = /* @__PURE__ */ defineComponent({
__name: "ResourcesListLayout",
props: {
resourceKey: {},
displayName: { type: Function, default: (resource) => resource.name || "" },
resources: {},
disabled: { type: Boolean },
initialize: { type: Function, default: async () => {
} },
filters: { default: () => ({ search: "", homeProject: "" }) },
additionalFiltersHandler: { type: Function, default: void 0 },
shareable: { type: Boolean, default: true },
sortFns: { default: () => ({}) },
sortOptions: { default: () => ["lastUpdated", "lastCreated", "nameAsc", "nameDesc"] },
type: { default: "list-full" },
typeProps: { default: () => ({ itemSize: 80 }) },
loading: { type: Boolean, default: true },
customPageSize: { default: 25 },
availablePageSizeOptions: { default: () => [10, 25, 50, 100] },
totalItems: { default: 0 },
resourcesRefreshing: { type: Boolean, default: false },
dontPerformSortingAndFiltering: { type: Boolean, default: false },
hasEmptyState: { type: Boolean, default: true },
uiConfig: { default: () => ({
searchEnabled: true,
showFiltersDropdown: true,
sortEnabled: true
}) }
},
emits: ["update:filters", "click:add", "update:pagination-and-sort", "update:search"],
setup(__props, { expose: __expose, emit: __emit }) {
const route = useRoute();
const router = useRouter();
const { callDebounced } = useDebounce();
const usersStore = useUsersStore();
const telemetry = useTelemetry();
const n8nLocalStorage = useN8nLocalStorage();
const props = __props;
const { getResourceText } = useResourcesListI18n(props.resourceKey);
const sortBy = ref(props.sortOptions[0]);
const hasFilters = ref(false);
const currentPage = ref(1);
const rowsPerPage = ref(props.customPageSize);
const resettingFilters = ref(false);
const search2 = ref(null);
const preferredPageSize = ref(props.customPageSize);
const preferredSort = ref(props.sortOptions[0]);
const emit = __emit;
useSlots();
const filtersModel = computed({
get: () => props.filters,
set: (newValue) => emit("update:filters", newValue)
});
const showEmptyState = computed(() => {
return props.hasEmptyState && props.resources.length === 0 && // Don't show empty state if resources are refreshing or if filters are being set
!hasFilters.value && !filtersModel.value.search && !props.resourcesRefreshing;
});
const filterKeys = computed(() => {
return Object.keys(filtersModel.value);
});
const filteredAndSortedResources = computed(() => {
if (props.dontPerformSortingAndFiltering) {
return props.resources;
}
const filtered = props.resources.filter((resource) => {
let matches = true;
if (filtersModel.value.homeProject && isSharedResource(resource)) {
matches = matches && !!("homeProject" in resource && resource.homeProject && resource.homeProject.id === filtersModel.value.homeProject);
}
if (filtersModel.value.search) {
const searchString = filtersModel.value.search.toLowerCase();
matches = matches && props.displayName(resource).toLowerCase().includes(searchString);
}
if (props.additionalFiltersHandler) {
matches = props.additionalFiltersHandler(resource, filtersModel.value, matches);
}
return matches;
});
return filtered.sort((a, b) => {
const sortableByDate = isResourceSortableByDate(a) && isResourceSortableByDate(b);
switch (sortBy.value) {
case "lastUpdated":
if (!sortableByDate) {
return 0;
}
if ("updatedAt" in a && "updatedAt" in b) {
return props.sortFns.lastUpdated ? props.sortFns.lastUpdated(a, b) : new Date(b.updatedAt ?? "").valueOf() - new Date(a.updatedAt ?? "").valueOf();
}
return 0;
case "lastCreated":
if (!sortableByDate) {
return 0;
}
if ("createdAt" in a && "createdAt" in b) {
return props.sortFns.lastCreated ? props.sortFns.lastCreated(a, b) : new Date(b.createdAt ?? "").valueOf() - new Date(a.createdAt ?? "").valueOf();
}
return 0;
case "nameAsc":
return props.sortFns.nameAsc ? props.sortFns.nameAsc(a, b) : props.displayName(a).trim().localeCompare(props.displayName(b).trim());
case "nameDesc":
return props.sortFns.nameDesc ? props.sortFns.nameDesc(a, b) : props.displayName(b).trim().localeCompare(props.displayName(a).trim());
default:
return props.sortFns[sortBy.value] ? props.sortFns[sortBy.value](a, b) : 0;
}
});
});
watch(
() => props.filters,
(value) => {
filtersModel.value = value;
if (hasAppliedFilters()) {
hasFilters.value = true;
}
}
);
watch(
() => filtersModel.value.homeProject,
() => {
sendFiltersTelemetry("homeProject");
}
);
watch(
() => filtersModel.value.tags,
() => {
sendFiltersTelemetry("tags");
}
);
watch(
() => filtersModel.value.type,
() => {
sendFiltersTelemetry("type");
}
);
watch(
() => filtersModel.value.search,
() => callDebounced(sendFiltersTelemetry, { debounceTime: 1e3, trailing: true }, "search")
);
watch(
() => filtersModel.value.setupNeeded,
() => {
sendFiltersTelemetry("setupNeeded");
}
);
watch(
() => filtersModel.value.incomplete,
() => {
sendFiltersTelemetry("incomplete");
}
);
watch([() => route.params?.projectId, () => route.name], async () => {
await resetFilters();
await loadPaginationPreferences();
await props.initialize();
});
onMounted(async () => {
await loadPaginationPreferences();
await props.initialize();
await nextTick();
if (hasAppliedFilters()) {
hasFilters.value = true;
}
window.addEventListener("keydown", captureSearchHotKey);
});
onBeforeUnmount(() => {
window.removeEventListener("keydown", captureSearchHotKey);
});
const captureSearchHotKey = (e) => {
if ((e.ctrlKey || e.metaKey) && e.key === "f") {
e.preventDefault();
focusSearchInput();
}
};
const focusSearchInput = () => {
if (search2.value) {
search2.value.focus();
}
};
const isFilterApplied = (key) => {
if (key === "search") return false;
if (typeof props.filters[key] === "boolean") {
return props.filters[key];
}
if (Array.isArray(props.filters[key])) {
return props.filters[key].length > 0;
}
return props.filters[key] !== "";
};
const hasOnlyFiltersThatShowMoreResults = computed(() => {
const activeFilters = filterKeys.value.filter(isFilterApplied);
const filtersThatShowMoreResults = ["showArchived"];
return activeFilters.every((filter) => {
return filtersThatShowMoreResults.includes(filter);
});
});
const hasAppliedFilters = () => {
return !!filterKeys.value.find(isFilterApplied);
};
const setRowsPerPage = async (numberOfRowsPerPage) => {
rowsPerPage.value = numberOfRowsPerPage;
await savePaginationPreferences();
emit("update:pagination-and-sort", {
pageSize: numberOfRowsPerPage
});
};
const setSorting = async (sort, persistUpdate = true) => {
sortBy.value = sort;
if (persistUpdate) {
await savePaginationPreferences();
}
emit("update:pagination-and-sort", {
sort
});
sendSortingTelemetry();
};
const setCurrentPage = async (page, persistUpdate = true) => {
currentPage.value = page;
if (persistUpdate) {
await savePaginationPreferences();
}
emit("update:pagination-and-sort", {
page
});
};
const sendFiltersTelemetry = (source) => {
if (resettingFilters.value) {
if (source !== "reset") {
return;
}
setTimeout(() => resettingFilters.value = false, 1500);
}
const filters2 = filtersModel.value;
const filtersSet = [];
const filterValues = [];
Object.keys(filters2).forEach((key) => {
if (filters2[key]) {
filtersSet.push(key);
filterValues.push(key === "search" ? null : filters2[key]);
}
});
telemetry.track(`User set filters in ${props.resourceKey} list`, {
filters_set: filtersSet,
filter_values: filterValues,
[`${props.resourceKey}_total_in_view`]: props.resources.length,
[`${props.resourceKey}_after_filtering`]: filteredAndSortedResources.value.length
});
};
const onAddButtonClick = (e) => {
emit("click:add", e);
};
const onUpdateFilters = (e) => {
emit("update:filters", e);
};
const resetFilters = async () => {
Object.keys(filtersModel.value).forEach((key) => {
filtersModel.value[key] = Array.isArray(filtersModel.value[key]) ? [] : "";
});
await setCurrentPage(1, false);
resettingFilters.value = true;
hasFilters.value = false;
sendFiltersTelemetry("reset");
emit("update:filters", filtersModel.value);
};
const itemSize = () => {
if ("itemSize" in props.typeProps) {
return props.typeProps.itemSize;
}
return 0;
};
const getColumns = () => {
if ("columns" in props.typeProps) {
return props.typeProps.columns;
}
return [];
};
const sendSortingTelemetry = () => {
telemetry.track(`User changed sorting in ${props.resourceKey} list`, {
sorting: sortBy.value
});
};
const onUpdateFiltersLength = (length) => {
hasFilters.value = length > 0;
};
const onSearch = (s) => {
filtersModel.value.search = s;
emit("update:search", s);
};
const findNearestPageSize = (size) => {
return props.availablePageSizeOptions.reduce(
(prev, curr) => Math.abs(curr - size) < Math.abs(prev - size) ? curr : prev
);
};
const savePaginationPreferences = async () => {
if (props.type !== "list-paginated") {
return;
}
const currentQuery = { ...route.query };
if (currentPage.value !== 1) {
currentQuery.page = currentPage.value.toString();
} else {
delete currentQuery.page;
}
if (rowsPerPage.value !== preferredPageSize.value) {
currentQuery.pageSize = rowsPerPage.value.toString();
preferredPageSize.value = rowsPerPage.value;
} else {
delete currentQuery.pageSize;
}
if (sortBy.value !== preferredSort.value) {
currentQuery.sort = sortBy.value;
preferredSort.value = sortBy.value;
} else {
delete currentQuery.sort;
}
n8nLocalStorage.saveProjectPreferencesToLocalStorage(
route.params.projectId ?? "",
"workflows",
{
sort: sortBy.value,
pageSize: rowsPerPage.value
}
);
await router.replace({
query: Object.keys(currentQuery).length ? currentQuery : void 0
});
};
const loadPaginationPreferences = async () => {
if (props.type !== "list-paginated") {
return;
}
const query = route.query;
const localStorageValues = n8nLocalStorage.loadProjectPreferencesFromLocalStorage(
route.params.projectId ?? "",
"workflows"
);
const emitPayload = {};
if (query.page) {
const newPage = parseInt(query.page, 10);
if (newPage > 1) {
currentPage.value = newPage;
emitPayload.page = newPage;
}
}
if (query.pageSize ?? localStorageValues.pageSize) {
const parsedSize = parseInt(
query.pageSize || String(localStorageValues.pageSize),
10
);
const newPageSize = findNearestPageSize(parsedSize);
rowsPerPage.value = newPageSize;
emitPayload.pageSize = newPageSize;
preferredPageSize.value = newPageSize;
} else {
rowsPerPage.value = props.customPageSize;
emitPayload.pageSize = props.customPageSize;
}
if (query.sort) {
sortBy.value = emitPayload.sort = preferredSort.value = query.sort;
} else if (localStorageValues.sort) {
await setSorting(localStorageValues.sort, false);
emitPayload.sort = localStorageValues.sort;
preferredSort.value = localStorageValues.sort;
} else {
sortBy.value = props.sortOptions[0];
}
emit("update:pagination-and-sort", emitPayload);
};
__expose({
currentPage,
setCurrentPage
});
return (_ctx, _cache) => {
const _component_n8n_loading = N8nLoading;
const _component_n8n_action_box = N8nActionBox;
const _component_n8n_icon = N8nIcon;
const _component_n8n_input = N8nInput;
const _component_n8n_option = _sfc_main$7;
const _component_n8n_select = N8nSelect;
const _component_n8n_link = N8nLink;
const _component_n8n_info_tip = InfoTip;
const _component_n8n_recycle_scroller = _sfc_main$4;
const _component_el_pagination = resolveComponent("el-pagination");
const _component_n8n_datatable = N8nDatatable;
const _component_n8n_text = N8nText;
return openBlock(), createBlock(PageViewLayout, null, {
header: withCtx(() => [
renderSlot(_ctx.$slots, "header", {}, void 0, true)
]),
default: withCtx(() => [
_ctx.loading ? (openBlock(), createElementBlock("div", _hoisted_1, [
createVNode(_component_n8n_loading, {
rows: 25,
"shrink-last": false
})
])) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
showEmptyState.value ? (openBlock(), createElementBlock("div", _hoisted_2, [
renderSlot(_ctx.$slots, "empty", {}, () => [
createVNode(_component_n8n_action_box, {
"data-test-id": "empty-resources-list",
emoji: "👋",
heading: unref(getResourceText)(
unref(usersStore).currentUser?.firstName ? "empty.heading" : "empty.heading.userNotSetup",
unref(usersStore).currentUser?.firstName ? "empty.heading" : "empty.heading.userNotSetup",
{ name: unref(usersStore).currentUser?.firstName ?? "" }
),
description: unref(getResourceText)("empty.description"),
"button-text": unref(getResourceText)("empty.button"),
"button-type": "secondary",
"button-disabled": _ctx.disabled,
"onClick:button": onAddButtonClick
}, {
disabledButtonTooltip: withCtx(() => [
createTextVNode(toDisplayString(unref(getResourceText)("empty.button.disabled.tooltip")), 1)
]),
_: 1
}, 8, ["heading", "description", "button-text", "button-disabled"])
], true)
])) : (openBlock(), createBlock(PageViewLayoutList, { key: 1 }, {
header: withCtx(() => [
createBaseVNode("div", {
class: normalizeClass(_ctx.$style["filters-row"])
}, [
createBaseVNode("div", {
class: normalizeClass(_ctx.$style.filters)
}, [
renderSlot(_ctx.$slots, "breadcrumbs", {}, void 0, true),
props.uiConfig.searchEnabled ? (openBlock(), createBlock(_component_n8n_input, {
key: 0,
ref_key: "search",
ref: search2,
"model-value": filtersModel.value.search,
class: normalizeClass(_ctx.$style.search),
placeholder: unref(getResourceText)("search.placeholder", "search.placeholder"),
size: "small",
clearable: "",
"data-test-id": "resources-list-search",
"onUpdate:modelValue": onSearch
}, {
prefix: withCtx(() => [
createVNode(_component_n8n_icon, { icon: "search" })
]),
_: 1
}, 8, ["model-value", "class", "placeholder"])) : createCommentVNode("", true),
props.uiConfig.sortEnabled ? (openBlock(), createElementBlock("div", {
key: 1,
class: normalizeClass(_ctx.$style["sort-and-filter"])
}, [
createVNode(_component_n8n_select, {
modelValue: sortBy.value,
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => sortBy.value = $event),
size: "small",
"data-test-id": "resources-list-sort",
onChange: _cache[1] || (_cache[1] = ($event) => setSorting(sortBy.value))
}, {
default: withCtx(() => [
(openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.sortOptions, (sortOption) => {
return openBlock(), createBlock(_component_n8n_option, {
key: sortOption,
"data-test-id": "resources-list-sort-item",
value: sortOption,
label: unref(getResourceText)(`sort.${sortOption}`, `sort.${sortOption}`)
}, null, 8, ["value", "label"]);
}), 128))
]),
_: 1
}, 8, ["modelValue"])
], 2)) : createCommentVNode("", true),
props.uiConfig.showFiltersDropdown ? (openBlock(), createElementBlock("div", {
key: 2,
class: normalizeClass(_ctx.$style["sort-and-filter"])
}, [
createVNode(ResourceFiltersDropdown, {
keys: filterKeys.value,
reset: resetFilters,
"model-value": filtersModel.value,
shareable: _ctx.shareable,
"just-icon": true,
"onUpdate:modelValue": onUpdateFilters,
"onUpdate:filtersLength": onUpdateFiltersLength
}, {
default: withCtx((resourceFiltersSlotProps) => [
renderSlot(_ctx.$slots, "filters", normalizeProps(guardReactiveProps(resourceFiltersSlotProps)), void 0, true)
]),
_: 3
}, 8, ["keys", "model-value", "shareable"]),
renderSlot(_ctx.$slots, "add-button", {}, void 0, true)
], 2)) : createCommentVNode("", true)
], 2)
], 2),
renderSlot(_ctx.$slots, "callout", {}, void 0, true),
props.uiConfig.showFiltersDropdown ? withDirectives((openBlock(), createElementBlock("div", _hoisted_3, [
createVNode(_component_n8n_info_tip, { bold: false }, {
default: withCtx(() => [
createTextVNode(toDisplayString(hasOnlyFiltersThatShowMoreResults.value ? unref(getResourceText)("filters.active.shortText", "filters.active.shortText") : unref(getResourceText)("filters.active", "filters.active")) + " ", 1),
createVNode(_component_n8n_link, {
"data-test-id": "workflows-filter-reset",
size: "small",
onClick: resetFilters
}, {
default: withCtx(() => [
createTextVNode(toDisplayString(unref(getResourceText)("filters.active.reset", "filters.active.reset")), 1)
]),
_: 1
})
]),
_: 1
})
], 512)), [
[vShow, hasFilters.value]
]) : createCommentVNode("", true),
_cache[4] || (_cache[4] = createBaseVNode("div", { class: "pb-xs" }, null, -1))
]),
default: withCtx(() => [
renderSlot(_ctx.$slots, "preamble", {}, void 0, true),
_ctx.resourcesRefreshing ? (openBlock(), createElementBlock("div", _hoisted_4, [
createVNode(_component_n8n_loading, {
rows: rowsPerPage.value,
"shrink-last": false
}, null, 8, ["rows"])
])) : filteredAndSortedResources.value.length > 0 ? (openBlock(), createElementBlock("div", {
key: 1,
ref: "listWrapperRef",
"data-test-id": "resources-list-wrapper",
class: normalizeClass(_ctx.$style.listWrapper)
}, [
_ctx.type === "list-full" ? (openBlock(), createBlock(_component_n8n_recycle_scroller, {
key: 0,
"data-test-id": "resources-list",
items: filteredAndSortedResources.value,
"item-size": itemSize(),
"item-key": "id"
}, {
default: withCtx(({ item, updateItemSize }) => [
renderSlot(_ctx.$slots, "default", {
data: item,
updateItemSize
}, void 0, true)
]),
_: 3
}, 8, ["items", "item-size"])) : _ctx.type === "list-paginated" ? (openBlock(), createElementBlock("div", {
key: 1,
class: normalizeClass(_ctx.$style.paginatedListWrapper),
"data-test-id": "paginated-list"
}, [
createBaseVNode("div", {
class: normalizeClass(_ctx.$style.listItems)
}, [
(openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.resources, (item, index) => {
return openBlock(), createElementBlock("div", {
key: index,
class: normalizeClass(_ctx.$style.listItem)
}, [
renderSlot(_ctx.$slots, "item", {
item,
index
}, () => [
createTextVNode(toDisplayString(item), 1)
], true)
], 2);
}), 128))
], 2),
createBaseVNode("div", {
class: normalizeClass(_ctx.$style.listPagination)
}, [
createVNode(_component_el_pagination, {
"current-page": currentPage.value,
"onUpdate:currentPage": [
_cache[2] || (_cache[2] = ($event) => currentPage.value = $event),
setCurrentPage
],
"page-size": rowsPerPage.value,
"onUpdate:pageSize": _cache[3] || (_cache[3] = ($event) => rowsPerPage.value = $event),
background: "",
total: _ctx.totalItems,
"page-sizes": _ctx.availablePageSizeOptions,
layout: "total, prev, pager, next, sizes",
"data-test-id": "resources-list-pagination",
onSizeChange: setRowsPerPage
}, null, 8, ["current-page", "page-size", "total", "page-sizes"])
], 2)
], 2)) : createCommentVNode("", true),
_ctx.type === "datatable" ? (openBlock(), createBlock(_component_n8n_datatable, {
key: 2,
"data-test-id": "resources-table",
class: normalizeClass(_ctx.$style.datatable),
columns: getColumns(),
rows: filteredAndSortedResources.value,
"current-page": currentPage.value,
"rows-per-page": rowsPerPage.value,
"onUpdate:currentPage": setCurrentPage,
"onUpdate:rowsPerPage": setRowsPerPage
}, {
row: withCtx(({ columns, row }) => [
renderSlot(_ctx.$slots, "default", {
data: row,
columns
}, void 0, true)
]),
_: 3
}, 8, ["class", "columns", "rows", "current-page", "rows-per-page"])) : createCommentVNode("", true)
], 2)) : hasAppliedFilters() || filtersModel.value.search !== "" ? (openBlock(), createBlock(_component_n8n_text, {
key: 2,
color: "text-base",
size: "medium",
"data-test-id": "resources-list-empty"
}, {
default: withCtx(() => [
createTextVNode(toDisplayString(unref(getResourceText)("noResults", "noResults")), 1)
]),
_: 1
})) : createCommentVNode("", true),
renderSlot(_ctx.$slots, "postamble", {}, void 0, true)
]),
_: 3
}))
], 64))
]),
_: 3
});
};
}
});
const filters = "_filters_10oee_123";
const search = "_search_10oee_154";
const listWrapper = "_listWrapper_10oee_167";
const paginatedListWrapper = "_paginatedListWrapper_10oee_173";
const listPagination = "_listPagination_10oee_181";
const datatable = "_datatable_10oee_208";
const style0 = {
"filters-row": "_filters-row_10oee_123",
filters,
"sort-and-filter": "_sort-and-filter_10oee_141",
search,
listWrapper,
paginatedListWrapper,
listPagination,
datatable
};
const cssModules = {
"$style": style0
};
const ResourcesListLayout = /* @__PURE__ */ _export_sfc(_sfc_main, [["__cssModules", cssModules], ["__scopeId", "data-v-fe845784"]]);
export {
ResourcesListLayout as R
};