UNPKG

n8n-editor-ui

Version:

Workflow Editor UI for n8n

710 lines (707 loc) 44.1 kB
; (function () { System.register(["./vue.runtime.esm-bundler-legacy--Sn1h8On.js", "./_MapCache-legacy-DlYNX_X1.js", "./src-legacy-uZTNWMC0.js", "./_plugin-vue_export-helper-legacy-DZMI5HPp.js", "./truncate-legacy-BGje52Or.js", "./useTelemetry-legacy-B3pzzkAE.js", "./useToast-legacy-C4Vu5mt9.js", "./constants-legacy-TKxWCqvM.js", "./folders.store-legacy-DbpbLgJq.js", "./ProjectIcon-legacy-BKmF89ad.js", "./readyToRun.store-legacy-D5fQDMhh.js"], function (_export, _context) { "use strict"; var computed, toDisplayString, createElementBlock, createCommentVNode, unref, ref, createVNode, defineComponent, createBlock, mergeProps, watch, renderSlot, withCtx, openBlock, createTextVNode, useTemplateRef, normalizeClass, createBaseVNode, useElementSize, useResizeObserver, useI18n, N8nButton_default, N8nText_default, N8nActionToggle_default, N8nTabs_default, N8nTooltip_default, N8nIconButton_default, N8nHeading_default, __plugin_vue_export_helper_default, useRoute, useRouter, useSourceControlStore, useProjectsStore, useUIStore, useSettingsStore, ProjectTypes, VARIABLE_MODAL_KEY, useUsersStore, useTelemetry, useToast, getResourcePermissions, VIEWS, PROJECT_DATA_TABLES, EnterpriseEditionFeature, useFoldersStore, isIconOrEmoji, ProjectIcon_default, useProjectPages, useReadyToRunStore, __vite_style__, ProjectTabs_vue_vue_type_script_setup_true_lang_default, ProjectTabs_default, ProjectCreateResource_vue_vue_type_script_setup_true_lang_default, buttonGroup, buttonGroupDropdown, ProjectCreateResource_vue_vue_type_style_index_0_lang_module_default, cssModules$1, ProjectCreateResource_default, truncateTextToFitWidth, ReadyToRunButton_vue_vue_type_script_setup_true_lang_default, ReadyToRunButton_default, _hoisted_1, ProjectHeader_vue_vue_type_script_setup_true_lang_default, projectHeader, projectDetails, actions, projectDescriptionWrapper, tooltip, headerActions, ProjectHeader_vue_vue_type_style_index_0_lang_module_default, cssModules, ProjectHeader_default; //#region src/app/utils/modules/tabUtils.ts /** * Process dynamic route configuration for tabs * Resolves dynamic routes with project IDs and other parameters */ /** * Process an array of tabs with dynamic route resolution */ function processDynamicTab(tab, projectId) { if (!tab.dynamicRoute) return tab; const tabRoute = { name: tab.dynamicRoute.name }; if (tab.dynamicRoute.includeProjectId && projectId) tabRoute.params = { projectId }; const { dynamicRoute, ...tabWithoutDynamic } = tab; return { ...tabWithoutDynamic, to: tabRoute }; } function processDynamicTabs(tabs, projectId) { return tabs.map(tab => processDynamicTab(tab, projectId)); } //#endregion //#region src/features/collaboration/projects/components/ProjectTabs.vue?vue&type=script&setup=true&lang.ts return { setters: [function (_vueRuntimeEsmBundlerLegacy003Js) { computed = _vueRuntimeEsmBundlerLegacy003Js.C; toDisplayString = _vueRuntimeEsmBundlerLegacy003Js.Cn; createElementBlock = _vueRuntimeEsmBundlerLegacy003Js.D; createCommentVNode = _vueRuntimeEsmBundlerLegacy003Js.E; unref = _vueRuntimeEsmBundlerLegacy003Js.Gt; ref = _vueRuntimeEsmBundlerLegacy003Js.It; createVNode = _vueRuntimeEsmBundlerLegacy003Js.M; defineComponent = _vueRuntimeEsmBundlerLegacy003Js.P; createBlock = _vueRuntimeEsmBundlerLegacy003Js.T; mergeProps = _vueRuntimeEsmBundlerLegacy003Js.W; watch = _vueRuntimeEsmBundlerLegacy003Js._t; renderSlot = _vueRuntimeEsmBundlerLegacy003Js.at; withCtx = _vueRuntimeEsmBundlerLegacy003Js.bt; openBlock = _vueRuntimeEsmBundlerLegacy003Js.et; createTextVNode = _vueRuntimeEsmBundlerLegacy003Js.j; useTemplateRef = _vueRuntimeEsmBundlerLegacy003Js.mt; normalizeClass = _vueRuntimeEsmBundlerLegacy003Js.vn; createBaseVNode = _vueRuntimeEsmBundlerLegacy003Js.w; }, function (_MapCacheLegacy005Js) { useElementSize = _MapCacheLegacy005Js.E; useResizeObserver = _MapCacheLegacy005Js.N; useI18n = _MapCacheLegacy005Js.gt; }, function (_srcLegacy007Js) { N8nButton_default = _srcLegacy007Js.Hn; N8nText_default = _srcLegacy007Js.Vn; N8nActionToggle_default = _srcLegacy007Js.et; N8nTabs_default = _srcLegacy007Js.g; N8nTooltip_default = _srcLegacy007Js.mt; N8nIconButton_default = _srcLegacy007Js.xt; N8nHeading_default = _srcLegacy007Js.zn; }, function (_pluginVue_exportHelperLegacy00fJs) { __plugin_vue_export_helper_default = _pluginVue_exportHelperLegacy00fJs.t; }, function (_truncateLegacy00hJs) { useRoute = _truncateLegacy00hJs.v; useRouter = _truncateLegacy00hJs.y; }, function (_useTelemetryLegacy00HJs) { useSourceControlStore = _useTelemetryLegacy00HJs.Dn; useProjectsStore = _useTelemetryLegacy00HJs.En; useUIStore = _useTelemetryLegacy00HJs.Fi; useSettingsStore = _useTelemetryLegacy00HJs.Ho; ProjectTypes = _useTelemetryLegacy00HJs.On; VARIABLE_MODAL_KEY = _useTelemetryLegacy00HJs.ko; useUsersStore = _useTelemetryLegacy00HJs.nr; useTelemetry = _useTelemetryLegacy00HJs.t; }, function (_useToastLegacy00JJs) { useToast = _useToastLegacy00JJs.t; }, function (_constantsLegacy00TJs) { getResourcePermissions = _constantsLegacy00TJs.Do; VIEWS = _constantsLegacy00TJs.Io; PROJECT_DATA_TABLES = _constantsLegacy00TJs.Jr; EnterpriseEditionFeature = _constantsLegacy00TJs.pc; }, function (_foldersStoreLegacy01DJs) { useFoldersStore = _foldersStoreLegacy01DJs.t; }, function (_ProjectIconLegacy02fJs) { isIconOrEmoji = _ProjectIconLegacy02fJs.n; ProjectIcon_default = _ProjectIconLegacy02fJs.t; }, function (_readyToRunStoreLegacy04fJs) { useProjectPages = _readyToRunStoreLegacy04fJs.n; useReadyToRunStore = _readyToRunStoreLegacy04fJs.t; }], execute: function () { __vite_style__ = document.createElement('style'); __vite_style__.textContent = "/* BEM support Func\n -------------------------- */\n/* Transition\n-------------------------- */\n/* Color\n-------------------------- */\n/* Link\n-------------------------- */\n/* Border\n-------------------------- */\n/* Outline\n-------------------------- */\n/* Box shadow\n-------------------------- */\n/* Fill\n-------------------------- */\n/* Typography\n-------------------------- */\n/* z-index\n-------------------------- */\n/* Disable base\n-------------------------- */\n/* Icon\n-------------------------- */\n/* Checkbox\n-------------------------- */\n/* Radio\n-------------------------- */\n/* Select\n-------------------------- */\n/* Alert\n-------------------------- */\n/* MessageBox\n-------------------------- */\n/* Message\n-------------------------- */\n/* Notification\n-------------------------- */\n/* Input\n-------------------------- */\n/* Cascader\n-------------------------- */\n/* Group\n-------------------------- */\n/* Tab\n-------------------------- */\n/* Button\n-------------------------- */\n/* cascader\n-------------------------- */\n/* Switch\n-------------------------- */\n/* Dialog\n-------------------------- */\n/* Table\n-------------------------- */\n/* Pagination\n-------------------------- */\n/* Popup\n-------------------------- */\n/* Popover\n-------------------------- */\n/* Tooltip\n-------------------------- */\n/* Tag\n-------------------------- */\n/* Tree\n-------------------------- */\n/* Dropdown\n-------------------------- */\n/* Badge\n-------------------------- */\n/* Card\n--------------------------*/\n/* Slider\n--------------------------*/\n/* Steps\n--------------------------*/\n/* Menu\n--------------------------*/\n/* Rate\n--------------------------*/\n/* DatePicker\n--------------------------*/\n/* Loading\n--------------------------*/\n/* Scrollbar\n--------------------------*/\n/* Carousel\n--------------------------*/\n/* Collapse\n--------------------------*/\n/* Transfer\n--------------------------*/\n/* Header\n --------------------------*/\n/* Footer\n--------------------------*/\n/* Main\n--------------------------*/\n/* Timeline\n--------------------------*/\n/* Backtop\n--------------------------*/\n/* Link\n--------------------------*/\n/* Calendar\n--------------------------*/\n/* Form\n-------------------------- */\n/* Avatar\n--------------------------*/\n/* Break-point\n--------------------------*/\n/* Break-points\n -------------------------- */\n/* Scrollbar\n -------------------------- */\n/* Placeholder\n -------------------------- */\n/* BEM\n -------------------------- */\n._buttonGroup_1ofs3_123 {\n display: inline-flex;\n}\n._buttonGroup_1ofs3_123 > .button {\n border-right: 1px solid var(--button--color--text, var(--button--color--text--primary));\n}\n._buttonGroup_1ofs3_123 > .button:not(:first-child) {\n border-radius: 0;\n}\n._buttonGroup_1ofs3_123 > .button:first-child {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n._buttonGroupDropdown_1ofs3_137 {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}/* BEM support Func\n -------------------------- */\n/* Transition\n-------------------------- */\n/* Color\n-------------------------- */\n/* Link\n-------------------------- */\n/* Border\n-------------------------- */\n/* Outline\n-------------------------- */\n/* Box shadow\n-------------------------- */\n/* Fill\n-------------------------- */\n/* Typography\n-------------------------- */\n/* z-index\n-------------------------- */\n/* Disable base\n-------------------------- */\n/* Icon\n-------------------------- */\n/* Checkbox\n-------------------------- */\n/* Radio\n-------------------------- */\n/* Select\n-------------------------- */\n/* Alert\n-------------------------- */\n/* MessageBox\n-------------------------- */\n/* Message\n-------------------------- */\n/* Notification\n-------------------------- */\n/* Input\n-------------------------- */\n/* Cascader\n-------------------------- */\n/* Group\n-------------------------- */\n/* Tab\n-------------------------- */\n/* Button\n-------------------------- */\n/* cascader\n-------------------------- */\n/* Switch\n-------------------------- */\n/* Dialog\n-------------------------- */\n/* Table\n-------------------------- */\n/* Pagination\n-------------------------- */\n/* Popup\n-------------------------- */\n/* Popover\n-------------------------- */\n/* Tooltip\n-------------------------- */\n/* Tag\n-------------------------- */\n/* Tree\n-------------------------- */\n/* Dropdown\n-------------------------- */\n/* Badge\n-------------------------- */\n/* Card\n--------------------------*/\n/* Slider\n--------------------------*/\n/* Steps\n--------------------------*/\n/* Menu\n--------------------------*/\n/* Rate\n--------------------------*/\n/* DatePicker\n--------------------------*/\n/* Loading\n--------------------------*/\n/* Scrollbar\n--------------------------*/\n/* Carousel\n--------------------------*/\n/* Collapse\n--------------------------*/\n/* Transfer\n--------------------------*/\n/* Header\n --------------------------*/\n/* Footer\n--------------------------*/\n/* Main\n--------------------------*/\n/* Timeline\n--------------------------*/\n/* Backtop\n--------------------------*/\n/* Link\n--------------------------*/\n/* Calendar\n--------------------------*/\n/* Form\n-------------------------- */\n/* Avatar\n--------------------------*/\n/* Break-point\n--------------------------*/\n/* Break-points\n -------------------------- */\n/* Scrollbar\n -------------------------- */\n/* Placeholder\n -------------------------- */\n/* BEM\n -------------------------- */\n._projectHeader_sx5k1_123 {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n min-height: var(--spacing--3xl);\n}\n._projectDetails_sx5k1_130 {\n display: flex;\n align-items: center;\n}\n._actions_sx5k1_135 {\n padding: var(--spacing--2xs) 0 var(--spacing--xs);\n}\n._projectDescriptionWrapper_sx5k1_139 {\n position: relative;\n display: inline-block;\n}\n._projectDescriptionWrapper_sx5k1_139:hover ._tooltip_sx5k1_143 {\n display: block;\n}\n._tooltip_sx5k1_143 {\n display: none;\n position: absolute;\n top: 0;\n left: calc(-1 * var(--spacing--3xs));\n background-color: var(--color--background--light-2);\n padding: 0 var(--spacing--3xs) var(--spacing--3xs);\n z-index: 10;\n white-space: normal;\n border-radius: 6px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\n}\n@media screen and (max-width: 767px) {\n._projectHeader_sx5k1_123 {\n flex-direction: column;\n align-items: flex-start;\n gap: var(--spacing--xs);\n}\n._headerActions_sx5k1_166 {\n margin-left: auto;\n}\n}/*$vite$:1*/"; document.head.appendChild(__vite_style__); ProjectTabs_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */defineComponent({ __name: "ProjectTabs", props: { showSettings: { type: Boolean, default: false }, showExecutions: { type: Boolean, default: true }, pageType: { default: "project" }, additionalTabs: { default: () => [] } }, setup(__props) { const props = __props; const locale = useI18n(); const route = useRoute(); const projectStore = useProjectsStore(); const telemetry = useTelemetry(); const selectedTab = ref(""); const selectedTabLabel = computed(() => selectedTab.value ? String(selectedTab.value) : ""); const projectId = computed(() => { return Array.isArray(route?.params?.projectId) ? route.params.projectId[0] : route?.params?.projectId; }); const isTeamProject = computed(() => projectStore.currentProject?.type === ProjectTypes.Team); const getRouteConfigs = () => { if (projectId.value) return { workflows: { name: VIEWS.PROJECTS_WORKFLOWS, params: { projectId: projectId.value } }, credentials: { name: VIEWS.PROJECTS_CREDENTIALS, params: { projectId: projectId.value } }, executions: { name: VIEWS.PROJECTS_EXECUTIONS, params: { projectId: projectId.value } }, variables: { name: VIEWS.PROJECTS_VARIABLES, params: { projectId: projectId.value } } }; if (props.pageType === "shared") return { workflows: { name: VIEWS.SHARED_WORKFLOWS }, credentials: { name: VIEWS.SHARED_CREDENTIALS }, executions: { name: VIEWS.NOT_FOUND }, variables: { name: VIEWS.NOT_FOUND } }; return { workflows: { name: VIEWS.WORKFLOWS }, credentials: { name: VIEWS.CREDENTIALS }, executions: { name: VIEWS.EXECUTIONS }, variables: { name: VIEWS.HOME_VARIABLES } }; }; const createTab = (label, routeKey, routes) => { return { label: locale.baseText(label), value: routes[routeKey].name, to: routes[routeKey] }; }; const options = computed(() => { const routes = getRouteConfigs(); const tabs = [createTab("mainSidebar.workflows", "workflows", routes), createTab("mainSidebar.credentials", "credentials", routes)]; if (props.showExecutions) tabs.push(createTab("mainSidebar.executions", "executions", routes)); if (props.pageType === "overview" || isTeamProject.value) tabs.push(createTab("mainSidebar.variables", "variables", routes)); if (props.additionalTabs?.length) { const processedAdditionalTabs = processDynamicTabs(props.additionalTabs, projectId.value); tabs.push(...processedAdditionalTabs); } if (props.showSettings) tabs.push({ label: locale.baseText("projects.settings"), value: VIEWS.PROJECT_SETTINGS, to: { name: VIEWS.PROJECT_SETTINGS, params: { projectId: projectId.value } } }); return tabs; }); watch(() => route?.name, () => { selectedTab.value = route.name === VIEWS.PROJECTS_FOLDERS ? VIEWS.PROJECTS_WORKFLOWS : route.name; }, { immediate: true }); function onSelectTab(value) { if (selectedTab.value === "variables") telemetry.track("User clicked project variables tab", { project_id: projectId.value }); selectedTab.value = value; } return (_ctx, _cache) => { return openBlock(), createBlock(unref(N8nTabs_default), { "model-value": selectedTabLabel.value, options: options.value, "data-test-id": "project-tabs", "onUpdate:modelValue": onSelectTab }, null, 8, ["model-value", "options"]); }; } }); //#endregion //#region src/features/collaboration/projects/components/ProjectTabs.vue ProjectTabs_default = ProjectTabs_vue_vue_type_script_setup_true_lang_default; //#endregion //#region src/features/collaboration/projects/components/ProjectCreateResource.vue?vue&type=script&setup=true&lang.ts ProjectCreateResource_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */defineComponent({ __name: "ProjectCreateResource", props: { actions: {}, disabled: { type: Boolean }, type: {} }, emits: ["action"], setup(__props, { expose: __expose, emit: __emit }) { const emit = __emit; const actionToggleRef = useTemplateRef("actionToggleRef"); __expose({ openActionToggle: isOpen => actionToggleRef.value?.openActionToggle(isOpen) }); return (_ctx, _cache) => { return openBlock(), createElementBlock("div", { class: normalizeClass([_ctx.$style.buttonGroup]) }, [renderSlot(_ctx.$slots, "default"), createVNode(unref(N8nActionToggle_default), { ref_key: "actionToggleRef", ref: actionToggleRef, "data-test-id": "add-resource", actions: _ctx.actions, placement: "bottom-end", teleported: false, onAction: _cache[0] || (_cache[0] = $event => emit("action", $event)) }, { default: withCtx(() => [createVNode(unref(N8nIconButton_default), { disabled: _ctx.disabled, class: normalizeClass([_ctx.$style.buttonGroupDropdown]), icon: "chevron-down", type: _ctx.type ?? "primary" }, null, 8, ["disabled", "class", "type"])]), _: 1 }, 8, ["actions"])], 2); }; } }); //#endregion //#region src/features/collaboration/projects/components/ProjectCreateResource.vue?vue&type=style&index=0&lang.module.scss buttonGroup = "_buttonGroup_1ofs3_123"; buttonGroupDropdown = "_buttonGroupDropdown_1ofs3_137"; ProjectCreateResource_vue_vue_type_style_index_0_lang_module_default = { buttonGroup, buttonGroupDropdown }; //#endregion //#region src/features/collaboration/projects/components/ProjectCreateResource.vue cssModules$1 = { "$style": ProjectCreateResource_vue_vue_type_style_index_0_lang_module_default }; ProjectCreateResource_default = /* @__PURE__ */__plugin_vue_export_helper_default(ProjectCreateResource_vue_vue_type_script_setup_true_lang_default, [["__cssModules", cssModules$1]]); //#endregion //#region src/app/utils/formatters/textFormatter.ts /** * Truncate text to fit within a specified width, adding an ellipsis if necessary. * @param text The text to truncate. * @param availableWidth The available width for the text in pixels. * @param fontSizeInPixels The font size of the text in pixels. * @returns The truncated text with ellipsis, or an empty string if the text fits within the available width. */ truncateTextToFitWidth = (text, availableWidth, fontSizeInPixels) => { if (!text || availableWidth <= 0) return ""; const averageCharWidth = .55 * fontSizeInPixels; const maxLengthToDisplay = Math.floor(availableWidth / averageCharWidth); if (text.length <= maxLengthToDisplay) return ""; const truncated = text.slice(0, maxLengthToDisplay); const lastSpaceIndex = truncated.lastIndexOf(" "); return truncated.slice(0, lastSpaceIndex === -1 ? maxLengthToDisplay : lastSpaceIndex) + "..."; }; //#endregion //#region src/features/workflows/readyToRun/components/ReadyToRunButton.vue?vue&type=script&setup=true&lang.ts ReadyToRunButton_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */defineComponent({ __name: "ReadyToRunButton", props: { hasActiveCallouts: { type: Boolean } }, setup(__props) { const props = __props; const route = useRoute(); const i18n = useI18n(); const toast = useToast(); const projectPages = useProjectPages(); const projectsStore = useProjectsStore(); const sourceControlStore = useSourceControlStore(); const foldersStore = useFoldersStore(); const readyToRunStore = useReadyToRunStore(); const projectPermissions = computed(() => { return getResourcePermissions(projectsStore.currentProject?.scopes ?? projectsStore.personalProject?.scopes); }); const showButton = computed(() => { return readyToRunStore.getButtonVisibility(foldersStore.totalWorkflowCount > 0, projectPermissions.value.workflow.create, sourceControlStore.preferences.branchReadOnly) && !props.hasActiveCallouts; }); const handleClick = async () => { const projectId = projectPages.isOverviewSubPage ? projectsStore.personalProject?.id : route.params.projectId; try { await readyToRunStore.claimCreditsAndOpenWorkflow("button", route.params.folderId, projectId); } catch (error) { toast.showError(error, i18n.baseText("generic.error")); } }; return (_ctx, _cache) => { return showButton.value ? (openBlock(), createBlock(unref(N8nButton_default), { key: 0, "data-test-id": "ready-to-run-button", type: "secondary", icon: "sparkles", loading: unref(readyToRunStore).claimingCredits, disabled: unref(sourceControlStore).preferences.branchReadOnly || unref(readyToRunStore).claimingCredits, onClick: handleClick }, { default: withCtx(() => [createTextVNode(toDisplayString(unref(i18n).baseText("workflows.empty.readyToRun")), 1)]), _: 1 }, 8, ["loading", "disabled"])) : createCommentVNode("", true); }; } }); //#endregion //#region src/features/workflows/readyToRun/components/ReadyToRunButton.vue ReadyToRunButton_default = ReadyToRunButton_vue_vue_type_script_setup_true_lang_default; //#endregion //#region src/features/collaboration/projects/components/ProjectHeader.vue?vue&type=script&setup=true&lang.ts _hoisted_1 = { style: { "display": "flex", "gap": "var(--spacing--xs)", "align-items": "center" } }; ProjectHeader_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */defineComponent({ __name: "ProjectHeader", props: { hasActiveCallouts: { type: Boolean }, mainButton: {} }, emits: ["createFolder"], setup(__props, { emit: __emit }) { const route = useRoute(); const router = useRouter(); const i18n = useI18n(); const projectsStore = useProjectsStore(); const sourceControlStore = useSourceControlStore(); const settingsStore = useSettingsStore(); const uiStore = useUIStore(); const telemetry = useTelemetry(); const usersStore = useUsersStore(); const projectPages = useProjectPages(); const props = __props; const emit = __emit; const headerIcon = computed(() => { if (projectsStore.currentProject?.type === ProjectTypes.Personal) return { type: "icon", value: "user" };else if (projectsStore.currentProject?.name) return isIconOrEmoji(projectsStore.currentProject.icon) ? projectsStore.currentProject.icon : { type: "icon", value: "layers" };else return { type: "icon", value: "house" }; }); const homeProject = computed(() => projectsStore.currentProject ?? projectsStore.personalProject); const isPersonalProject = computed(() => { return homeProject.value?.type === ProjectTypes.Personal; }); const projectName = computed(() => { if (!projectsStore.currentProject) { if (projectPages.isSharedSubPage) return i18n.baseText("projects.header.shared.title");else if (projectPages.isOverviewSubPage) return i18n.baseText("projects.menu.overview");else if (isPersonalProject.value) return i18n.baseText("projects.menu.personal"); return null; } else if (projectsStore.currentProject.type === ProjectTypes.Personal) return i18n.baseText("projects.menu.personal");else return projectsStore.currentProject.name; }); const projectPermissions = computed(() => getResourcePermissions(projectsStore.currentProject?.scopes).project); const projectVariablePermissions = computed(() => getResourcePermissions(projectsStore.currentProject?.scopes).projectVariable); const globalVariablesPermissions = computed(() => getResourcePermissions(usersStore.currentUser?.globalScopes).variable); const showSettings = computed(() => !!route?.params?.projectId && !!projectPermissions.value.update && projectsStore.currentProject?.type === ProjectTypes.Team); const showFolders = computed(() => { return settingsStore.isFoldersFeatureEnabled && [VIEWS.PROJECTS_WORKFLOWS, VIEWS.PROJECTS_FOLDERS].includes(route.name); }); const customProjectTabs = computed(() => { let tabType; if (projectPages.isSharedSubPage) tabType = "shared";else if (projectPages.isOverviewSubPage) tabType = "overview";else tabType = "project"; return Object.keys(uiStore.moduleTabs[tabType]).filter(settingsStore.isModuleActive).flatMap(module => uiStore.moduleTabs[tabType][module]); }); const ACTION_TYPES = { WORKFLOW: "workflow", CREDENTIAL: "credential", FOLDER: "folder", DATA_TABLE: "dataTable", VARIABLE: "variable" }; const createWorkflowButton = computed(() => ({ value: ACTION_TYPES.WORKFLOW, label: i18n.baseText("projects.header.create.workflow"), icon: sourceControlStore.preferences.branchReadOnly ? "lock" : void 0, size: "mini", disabled: sourceControlStore.preferences.branchReadOnly || !getResourcePermissions(homeProject.value?.scopes).workflow.create })); const createCredentialButton = computed(() => ({ value: ACTION_TYPES.CREDENTIAL, label: i18n.baseText("projects.header.create.credential"), icon: sourceControlStore.preferences.branchReadOnly ? "lock" : void 0, size: "mini", disabled: sourceControlStore.preferences.branchReadOnly || !getResourcePermissions(homeProject.value?.scopes).credential.create })); const createDataTableButton = computed(() => ({ value: ACTION_TYPES.DATA_TABLE, label: i18n.baseText("dataTable.add.button.label"), icon: sourceControlStore.preferences.branchReadOnly ? "lock" : void 0, size: "mini", disabled: sourceControlStore.preferences.branchReadOnly || !getResourcePermissions(homeProject.value?.scopes)?.dataTable?.create })); const createVariableButton = computed(() => ({ value: ACTION_TYPES.VARIABLE, label: i18n.baseText("variables.add.button.label"), icon: sourceControlStore.preferences.branchReadOnly ? "lock" : void 0, size: "mini", disabled: sourceControlStore.preferences.branchReadOnly || !projectVariablePermissions.value.create && !globalVariablesPermissions.value.create })); const selectedMainButtonType = computed(() => props.mainButton ?? ACTION_TYPES.WORKFLOW); const mainButtonConfig = computed(() => { switch (selectedMainButtonType.value) { case ACTION_TYPES.CREDENTIAL: return createCredentialButton.value; case ACTION_TYPES.DATA_TABLE: return createDataTableButton.value; case ACTION_TYPES.VARIABLE: return createVariableButton.value; case ACTION_TYPES.WORKFLOW: default: return createWorkflowButton.value; } }); const menu = computed(() => { const items = []; if (selectedMainButtonType.value !== ACTION_TYPES.WORKFLOW) items.push({ value: ACTION_TYPES.WORKFLOW, label: i18n.baseText("projects.header.create.workflow"), disabled: sourceControlStore.preferences.branchReadOnly || !getResourcePermissions(homeProject.value?.scopes).workflow.create }); if (selectedMainButtonType.value !== ACTION_TYPES.CREDENTIAL) items.push({ value: ACTION_TYPES.CREDENTIAL, label: i18n.baseText("projects.header.create.credential"), disabled: sourceControlStore.preferences.branchReadOnly || !getResourcePermissions(homeProject.value?.scopes).credential.create }); if (selectedMainButtonType.value !== ACTION_TYPES.VARIABLE && settingsStore.isEnterpriseFeatureEnabled[EnterpriseEditionFeature.Variables]) items.push({ value: ACTION_TYPES.VARIABLE, label: i18n.baseText("variables.add.button.label"), disabled: sourceControlStore.preferences.branchReadOnly || !getResourcePermissions(homeProject.value?.scopes).projectVariable.create }); if (showFolders.value) items.push({ value: ACTION_TYPES.FOLDER, label: i18n.baseText("projects.header.create.folder"), disabled: sourceControlStore.preferences.branchReadOnly || !getResourcePermissions(homeProject.value?.scopes).folder.create }); if (settingsStore.isDataTableFeatureEnabled && selectedMainButtonType.value !== ACTION_TYPES.DATA_TABLE) items.push({ value: ACTION_TYPES.DATA_TABLE, label: i18n.baseText("dataTable.add.button.label"), disabled: sourceControlStore.preferences.branchReadOnly || !getResourcePermissions(homeProject.value?.scopes)?.dataTable?.create }); return items; }); const showProjectIcon = computed(() => { return !projectPages.isOverviewSubPage && !projectPages.isSharedSubPage && !isPersonalProject.value; }); function isCredentialsListView(routeName) { return [VIEWS.PROJECTS_CREDENTIALS, VIEWS.CREDENTIALS, VIEWS.SHARED_CREDENTIALS].includes(routeName); } function isWorkflowListView(routeName) { return [VIEWS.PROJECTS_WORKFLOWS, VIEWS.WORKFLOWS, VIEWS.SHARED_WORKFLOWS, VIEWS.PROJECTS_FOLDERS].includes(routeName); } function getUIContext(routeName) { if (isCredentialsListView(routeName)) return "credentials_list";else if (isWorkflowListView(routeName)) return "workflow_list";else return; } const actions$1 = { [ACTION_TYPES.WORKFLOW]: projectId => { router.push({ name: VIEWS.NEW_WORKFLOW, query: { projectId, parentFolderId: route.params.folderId, uiContext: getUIContext(route.name?.toString() ?? "") } }); }, [ACTION_TYPES.CREDENTIAL]: projectId => { router.push({ name: VIEWS.PROJECTS_CREDENTIALS, params: { projectId, credentialId: "create" }, query: { uiContext: getUIContext(route.name?.toString() ?? "") } }); }, [ACTION_TYPES.FOLDER]: () => { emit("createFolder"); }, [ACTION_TYPES.DATA_TABLE]: projectId => { router.push({ name: PROJECT_DATA_TABLES, params: { projectId, new: "new" } }); }, [ACTION_TYPES.VARIABLE]: () => { uiStore.openModalWithData({ name: VARIABLE_MODAL_KEY, data: { mode: "new" } }); telemetry.track("User clicked header add variable button"); } }; const pageType = computed(() => { if (projectPages.isSharedSubPage) return "shared";else if (projectPages.isOverviewSubPage) return "overview";else return "project"; }); const sectionDescription = computed(() => { if (projectPages.isSharedSubPage) return i18n.baseText("projects.header.shared.subtitle");else if (projectPages.isOverviewSubPage) return i18n.baseText(settingsStore.isDataTableFeatureEnabled ? "projects.header.overview.subtitleWithDataTables" : "projects.header.overview.subtitle");else if (isPersonalProject.value) return i18n.baseText(settingsStore.isDataTableFeatureEnabled ? "projects.header.personal.subtitleWithDataTables" : "projects.header.personal.subtitle"); return null; }); const projectDescription = computed(() => { if (projectPages.isProjectsSubPage) return projectsStore.currentProject?.description; return null; }); const projectHeaderRef = ref(null); const { width: projectHeaderWidth } = useElementSize(projectHeaderRef); const headerActionsRef = ref(null); const { width: headerActionsWidth } = useElementSize(headerActionsRef); const projectSubtitleFontSizeInPxs = ref(null); useResizeObserver(projectHeaderRef, () => { if (!projectHeaderRef.value) return; const projectSubtitleEl = projectHeaderRef.value.querySelector("span[data-test-id=\"project-subtitle\"]"); if (projectSubtitleEl) { const computedStyle = window.getComputedStyle(projectSubtitleEl); projectSubtitleFontSizeInPxs.value = parseFloat(computedStyle.fontSize); } }); const projectDescriptionTruncated = computed(() => { if (!projectDescription.value) return ""; const availableTextWidth = projectHeaderWidth.value - headerActionsWidth.value; const fontSizeInPixels = projectSubtitleFontSizeInPxs.value ?? 14; return truncateTextToFitWidth(projectDescription.value, availableTextWidth, fontSizeInPixels); }); const onSelect = action => { const executableAction = actions$1[action]; if (!homeProject.value) return; executableAction(homeProject.value.id); }; return (_ctx, _cache) => { return openBlock(), createElementBlock("div", null, [createBaseVNode("div", { ref_key: "projectHeaderRef", ref: projectHeaderRef, class: normalizeClass(_ctx.$style.projectHeader) }, [createBaseVNode("div", { class: normalizeClass(_ctx.$style.projectDetails) }, [showProjectIcon.value ? (openBlock(), createBlock(ProjectIcon_default, { key: 0, icon: headerIcon.value, "border-less": true, size: "medium" }, null, 8, ["icon"])) : createCommentVNode("", true), createBaseVNode("div", { class: normalizeClass(_ctx.$style.headerActions) }, [projectName.value ? (openBlock(), createBlock(unref(N8nHeading_default), { key: 0, bold: "", tag: "h2", size: "xlarge", "data-test-id": "project-name" }, { default: withCtx(() => [createTextVNode(toDisplayString(projectName.value), 1)]), _: 1 })) : createCommentVNode("", true), sectionDescription.value ? (openBlock(), createBlock(unref(N8nText_default), { key: 1, color: "text-light", "data-test-id": "project-subtitle" }, { default: withCtx(() => [createTextVNode(toDisplayString(sectionDescription.value), 1)]), _: 1 })) : projectDescription.value ? (openBlock(), createElementBlock("div", { key: 2, class: normalizeClass(_ctx.$style.projectDescriptionWrapper) }, [createVNode(unref(N8nText_default), { color: "text-light", "data-test-id": "project-subtitle" }, { default: withCtx(() => [createTextVNode(toDisplayString(projectDescriptionTruncated.value || projectDescription.value), 1)]), _: 1 }), projectDescriptionTruncated.value ? (openBlock(), createElementBlock("div", { key: 0, class: normalizeClass(_ctx.$style.tooltip) }, [createVNode(unref(N8nText_default), { color: "text-light" }, { default: withCtx(() => [createTextVNode(toDisplayString(projectDescription.value), 1)]), _: 1 })], 2)) : createCommentVNode("", true)], 2)) : createCommentVNode("", true)], 2)], 2), unref(route).name !== unref(VIEWS).PROJECT_SETTINGS ? (openBlock(), createElementBlock("div", { key: 0, ref_key: "headerActionsRef", ref: headerActionsRef, class: normalizeClass([_ctx.$style.headerActions]) }, [createVNode(unref(N8nTooltip_default), { disabled: !unref(sourceControlStore).preferences.branchReadOnly, content: unref(i18n).baseText("readOnlyEnv.cantAdd.any") }, { default: withCtx(() => [createBaseVNode("div", _hoisted_1, [createVNode(ReadyToRunButton_default, { "has-active-callouts": props.hasActiveCallouts }, null, 8, ["has-active-callouts"]), createVNode(ProjectCreateResource_default, { "data-test-id": "add-resource-buttons", actions: menu.value, disabled: unref(sourceControlStore).preferences.branchReadOnly, onAction: onSelect }, { default: withCtx(() => [createVNode(unref(N8nButton_default), mergeProps({ "data-test-id": `add-resource-${selectedMainButtonType.value}` }, mainButtonConfig.value, { onClick: _cache[0] || (_cache[0] = $event => onSelect(selectedMainButtonType.value)) }), null, 16, ["data-test-id"])]), _: 1 }, 8, ["actions", "disabled"])])]), _: 1 }, 8, ["disabled", "content"])], 2)) : createCommentVNode("", true)], 2), renderSlot(_ctx.$slots, "default"), createBaseVNode("div", { class: normalizeClass(_ctx.$style.actions) }, [createVNode(ProjectTabs_default, { "page-type": pageType.value, "show-executions": !unref(projectPages).isSharedSubPage, "show-settings": showSettings.value, "additional-tabs": customProjectTabs.value }, null, 8, ["page-type", "show-executions", "show-settings", "additional-tabs"])], 2)]); }; } }); //#endregion //#region src/features/collaboration/projects/components/ProjectHeader.vue?vue&type=style&index=0&lang.module.scss projectHeader = "_projectHeader_sx5k1_123"; projectDetails = "_projectDetails_sx5k1_130"; actions = "_actions_sx5k1_135"; projectDescriptionWrapper = "_projectDescriptionWrapper_sx5k1_139"; tooltip = "_tooltip_sx5k1_143"; headerActions = "_headerActions_sx5k1_166"; ProjectHeader_vue_vue_type_style_index_0_lang_module_default = { projectHeader, projectDetails, actions, projectDescriptionWrapper, tooltip, headerActions }; //#endregion //#region src/features/collaboration/projects/components/ProjectHeader.vue cssModules = { "$style": ProjectHeader_vue_vue_type_style_index_0_lang_module_default }; _export("t", ProjectHeader_default = /* @__PURE__ */__plugin_vue_export_helper_default(ProjectHeader_vue_vue_type_script_setup_true_lang_default, [["__cssModules", cssModules]])); //#endregion } }; }); })();