mirror of
https://github.com/n8n-io/n8n.git
synced 2026-07-28 03:24:59 +02:00
sidebar component structure
This commit is contained in:
parent
85576f5d93
commit
6a5b523d64
|
|
@ -0,0 +1,662 @@
|
|||
import type { StoryFn } from '@storybook/vue3';
|
||||
|
||||
import Sidebar from './Sidebar.vue';
|
||||
import { type TreeItemType } from '.';
|
||||
|
||||
export default {
|
||||
title: 'Sidebar/Sidebar',
|
||||
component: Sidebar,
|
||||
};
|
||||
|
||||
const mockPersonalItems: TreeItemType[] = [
|
||||
{
|
||||
id: 'personal-folder-1',
|
||||
label: 'Customer Management',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: 'personal-subfolder-1',
|
||||
label: 'Onboarding Processes',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: 'personal-subfolder-1-1',
|
||||
label: 'New Customer Flows',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: 'personal-subfolder-1-1-1',
|
||||
label: 'Enterprise Customers',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{ id: 'workflow-1', label: 'Enterprise Customer Onboarding', type: 'workflow' },
|
||||
{ id: 'workflow-2', label: 'Enterprise Account Setup', type: 'workflow' },
|
||||
{ id: 'workflow-3', label: 'Enterprise Training Schedule', type: 'workflow' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'personal-subfolder-1-1-2',
|
||||
label: 'Small Business Customers',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{ id: 'workflow-4', label: 'SMB Quick Onboarding', type: 'workflow' },
|
||||
{ id: 'workflow-5', label: 'SMB Account Verification', type: 'workflow' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'personal-subfolder-1-2',
|
||||
label: 'Returning Customer Flows',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{ id: 'workflow-6', label: 'Account Reactivation', type: 'workflow' },
|
||||
{ id: 'workflow-7', label: 'Update Customer Info', type: 'workflow' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'personal-subfolder-2',
|
||||
label: 'Support Operations',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: 'personal-subfolder-2-1',
|
||||
label: 'Ticket Management',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: 'personal-subfolder-2-1-1',
|
||||
label: 'Priority Handling',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{ id: 'workflow-8', label: 'Critical Issue Handler', type: 'workflow' },
|
||||
{ id: 'workflow-9', label: 'Urgent Escalation', type: 'workflow' },
|
||||
],
|
||||
},
|
||||
{ id: 'workflow-10', label: 'General Support Tickets', type: 'workflow' },
|
||||
],
|
||||
},
|
||||
{ id: 'workflow-11', label: 'Customer Satisfaction Survey', type: 'workflow' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'personal-folder-2',
|
||||
label: 'Analytics & Reporting',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: 'personal-analytics-1',
|
||||
label: 'Sales Analytics',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: 'personal-analytics-1-1',
|
||||
label: 'Daily Reports',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: 'personal-analytics-1-1-1',
|
||||
label: 'Revenue Tracking',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: 'personal-analytics-1-1-1-1',
|
||||
label: 'Product Line Analysis',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{ id: 'workflow-12', label: 'Software Revenue', type: 'workflow' },
|
||||
{ id: 'workflow-13', label: 'Service Revenue', type: 'workflow' },
|
||||
{ id: 'workflow-14', label: 'Subscription Revenue', type: 'workflow' },
|
||||
],
|
||||
},
|
||||
{ id: 'workflow-15', label: 'Daily Revenue Report', type: 'workflow' },
|
||||
],
|
||||
},
|
||||
{ id: 'workflow-16', label: 'Sales Performance', type: 'workflow' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'personal-analytics-1-2',
|
||||
label: 'Weekly Reports',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{ id: 'workflow-17', label: 'Weekly Sales Summary', type: 'workflow' },
|
||||
{ id: 'workflow-18', label: 'Team Performance Review', type: 'workflow' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'personal-analytics-2',
|
||||
label: 'User Analytics',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: 'personal-analytics-2-1',
|
||||
label: 'Behavior Analysis',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{ id: 'workflow-19', label: 'User Activity Analysis', type: 'workflow' },
|
||||
{ id: 'workflow-20', label: 'Feature Usage Tracking', type: 'workflow' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'personal-folder-3',
|
||||
label: 'Development & Testing',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: 'personal-dev-1',
|
||||
label: 'Automated Testing',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: 'personal-dev-1-1',
|
||||
label: 'Unit Tests',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: 'personal-dev-1-1-1',
|
||||
label: 'Frontend Tests',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: 'personal-dev-1-1-1-1',
|
||||
label: 'Component Tests',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{ id: 'workflow-21', label: 'Button Component Test', type: 'workflow' },
|
||||
{ id: 'workflow-22', label: 'Form Component Test', type: 'workflow' },
|
||||
{ id: 'workflow-23', label: 'Modal Component Test', type: 'workflow' },
|
||||
],
|
||||
},
|
||||
{ id: 'workflow-24', label: 'Service Tests', type: 'workflow' },
|
||||
],
|
||||
},
|
||||
{ id: 'workflow-25', label: 'Backend API Tests', type: 'workflow' },
|
||||
],
|
||||
},
|
||||
{ id: 'workflow-26', label: 'Integration Tests', type: 'workflow' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'personal-dev-2',
|
||||
label: 'Empty Folder Test',
|
||||
type: 'folder',
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{ id: 'workflow-27', label: 'Quick Data Sync', type: 'workflow' },
|
||||
];
|
||||
|
||||
const mockSharedItems: TreeItemType[] = [
|
||||
{
|
||||
id: 'shared-folder-1',
|
||||
label: 'Team Workflows',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: 'shared-subfolder-1',
|
||||
label: 'Development Team',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: 'shared-subfolder-1-1',
|
||||
label: 'Sprint Management',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: 'shared-subfolder-1-1-1',
|
||||
label: 'Daily Operations',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: 'shared-subfolder-1-1-1-1',
|
||||
label: 'Standup Automation',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{ id: 'workflow-28', label: 'Daily Standup Prep', type: 'workflow' },
|
||||
{ id: 'workflow-29', label: 'Standup Summary Generator', type: 'workflow' },
|
||||
{ id: 'workflow-30', label: 'Standup Follow-up Tasks', type: 'workflow' },
|
||||
],
|
||||
},
|
||||
{ id: 'workflow-31', label: 'Sprint Velocity Tracking', type: 'workflow' },
|
||||
],
|
||||
},
|
||||
{ id: 'workflow-32', label: 'Sprint Planning Assistant', type: 'workflow' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'shared-subfolder-1-2',
|
||||
label: 'Code Quality',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: 'shared-subfolder-1-2-1',
|
||||
label: 'Review Processes',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{ id: 'workflow-33', label: 'PR Review Assignment', type: 'workflow' },
|
||||
{ id: 'workflow-34', label: 'Code Review Notifications', type: 'workflow' },
|
||||
{ id: 'workflow-35', label: 'Review Completion Tracker', type: 'workflow' },
|
||||
],
|
||||
},
|
||||
{ id: 'workflow-36', label: 'Code Quality Metrics', type: 'workflow' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'shared-subfolder-2',
|
||||
label: 'DevOps Team',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: 'shared-subfolder-2-1',
|
||||
label: 'Infrastructure',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: 'shared-subfolder-2-1-1',
|
||||
label: 'Monitoring & Alerts',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: 'shared-subfolder-2-1-1-1',
|
||||
label: 'System Health',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{ id: 'workflow-37', label: 'Server Health Check', type: 'workflow' },
|
||||
{
|
||||
id: 'workflow-38',
|
||||
label: 'Database Performance Monitor',
|
||||
type: 'workflow',
|
||||
},
|
||||
{ id: 'workflow-39', label: 'API Response Time Tracker', type: 'workflow' },
|
||||
],
|
||||
},
|
||||
{ id: 'workflow-40', label: 'Alert Management System', type: 'workflow' },
|
||||
],
|
||||
},
|
||||
{ id: 'workflow-41', label: 'Deployment Pipeline Monitor', type: 'workflow' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'shared-empty-folder',
|
||||
label: 'Archive (Empty for Testing)',
|
||||
type: 'folder',
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'shared-folder-2',
|
||||
label: 'Cross-Team Initiatives',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: 'shared-folder-2-1',
|
||||
label: 'Company-wide Projects',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: 'shared-folder-2-1-1',
|
||||
label: 'Digital Transformation',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: 'shared-folder-2-1-1-1',
|
||||
label: 'Process Automation',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: 'shared-folder-2-1-1-1-1',
|
||||
label: 'HR Automation',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: 'workflow-42',
|
||||
label: 'Employee Onboarding Automation',
|
||||
type: 'workflow',
|
||||
},
|
||||
{ id: 'workflow-43', label: 'Leave Request Processing', type: 'workflow' },
|
||||
{
|
||||
id: 'workflow-44',
|
||||
label: 'Performance Review Scheduler',
|
||||
type: 'workflow',
|
||||
},
|
||||
],
|
||||
},
|
||||
{ id: 'workflow-45', label: 'Finance Approval Workflows', type: 'workflow' },
|
||||
],
|
||||
},
|
||||
{ id: 'workflow-46', label: 'Digital Asset Management', type: 'workflow' },
|
||||
],
|
||||
},
|
||||
{ id: 'workflow-47', label: 'Quarterly Business Review Generator', type: 'workflow' },
|
||||
],
|
||||
},
|
||||
{ id: 'workflow-48', label: 'All-Hands Meeting Prep', type: 'workflow' },
|
||||
],
|
||||
},
|
||||
{ id: 'workflow-49', label: 'Shared Dashboard Update', type: 'workflow' },
|
||||
];
|
||||
|
||||
const mockProjects = [
|
||||
{
|
||||
id: 'project-1',
|
||||
title: 'E-commerce Platform',
|
||||
items: [
|
||||
{
|
||||
id: 'ecommerce-folder-1',
|
||||
label: 'Order Processing',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{ id: 'workflow-9', label: 'Order Confirmation', type: 'workflow' },
|
||||
{ id: 'workflow-10', label: 'Inventory Update', type: 'workflow' },
|
||||
{ id: 'workflow-11', label: 'Shipping Notification', type: 'workflow' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'ecommerce-folder-2',
|
||||
label: 'Customer Communication',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: 'workflow-12',
|
||||
label: 'Welcome Email Sequence',
|
||||
type: 'workflow',
|
||||
icon: 'workflow',
|
||||
},
|
||||
{
|
||||
id: 'workflow-13',
|
||||
label: 'Abandoned Cart Recovery',
|
||||
type: 'workflow',
|
||||
icon: 'workflow',
|
||||
},
|
||||
],
|
||||
},
|
||||
] as TreeItemType[],
|
||||
},
|
||||
{
|
||||
id: 'project-2',
|
||||
title: 'Marketing Automation',
|
||||
items: [
|
||||
{ id: 'workflow-14', label: 'Lead Scoring', type: 'workflow' },
|
||||
{
|
||||
id: 'workflow-15',
|
||||
label: 'Campaign Performance Tracker',
|
||||
type: 'workflow',
|
||||
icon: 'workflow',
|
||||
},
|
||||
{
|
||||
id: 'marketing-folder-1',
|
||||
label: 'Social Media',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{ id: 'workflow-16', label: 'Auto Post Scheduler', type: 'workflow' },
|
||||
{ id: 'workflow-17', label: 'Engagement Tracker', type: 'workflow' },
|
||||
],
|
||||
},
|
||||
] as TreeItemType[],
|
||||
},
|
||||
{
|
||||
id: 'project-3',
|
||||
title: 'Stress Test - Deep Nesting',
|
||||
items: [
|
||||
{
|
||||
id: 'stress-test-folder',
|
||||
label: 'Level 1 - Root Organization',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: 'stress-level-2',
|
||||
label: 'Level 2 - Department Structure',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: 'stress-level-3',
|
||||
label:
|
||||
'Level 3 - Team Subdivisions with Very Long Descriptive Names That Test UI Wrapping',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: 'stress-level-4',
|
||||
label:
|
||||
'Level 4 - Project Categories and Workflow Types for Complex Business Process Management',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: 'stress-level-5',
|
||||
label:
|
||||
'Level 5 - Specific Implementation Details and Granular Task Definitions',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: 'stress-level-6',
|
||||
label:
|
||||
'Level 6 - Final Implementation Layer with Atomic Workflow Components',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: 'stress-workflow-1',
|
||||
label: 'Ultra-Deep Nested Workflow #1 - Data Processing Pipeline',
|
||||
type: 'workflow',
|
||||
},
|
||||
{
|
||||
id: 'stress-workflow-2',
|
||||
label: 'Ultra-Deep Nested Workflow #2 - Error Handling System',
|
||||
type: 'workflow',
|
||||
},
|
||||
{
|
||||
id: 'stress-workflow-3',
|
||||
label:
|
||||
'Ultra-Deep Nested Workflow #3 - Performance Monitoring Suite',
|
||||
type: 'workflow',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'stress-workflow-4',
|
||||
label: 'Level 5 Direct Workflow - Integration Manager',
|
||||
type: 'workflow',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'stress-workflow-5',
|
||||
label: 'Level 4 Direct Workflow - Configuration Handler',
|
||||
type: 'workflow',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'stress-level-4-alternate',
|
||||
label: 'Level 4 Alternate - Another Deep Branch for Comprehensive Testing',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: 'stress-empty-deep',
|
||||
label: 'Empty Folder at Level 5 - Testing Edge Cases',
|
||||
type: 'folder',
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
id: 'stress-workflow-6',
|
||||
label: 'Alternative Branch Workflow - Backup Systems',
|
||||
type: 'workflow',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'stress-parallel-branch',
|
||||
label: 'Level 3 Parallel Branch - Additional Complexity',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: 'stress-workflow-7',
|
||||
label: 'Parallel Branch Workflow - Load Balancer',
|
||||
type: 'workflow',
|
||||
},
|
||||
{
|
||||
id: 'stress-workflow-8',
|
||||
label: 'Parallel Branch Workflow - Cache Manager',
|
||||
type: 'workflow',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'stress-many-items',
|
||||
label: 'Level 2 - Folder with Many Direct Children',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: 'many-workflow-1',
|
||||
label: 'Bulk Test Workflow #01 - First in Series',
|
||||
type: 'workflow',
|
||||
},
|
||||
{
|
||||
id: 'many-workflow-2',
|
||||
label: 'Bulk Test Workflow #02 - Second in Series',
|
||||
type: 'workflow',
|
||||
},
|
||||
{
|
||||
id: 'many-workflow-3',
|
||||
label: 'Bulk Test Workflow #03 - Third in Series',
|
||||
type: 'workflow',
|
||||
},
|
||||
{
|
||||
id: 'many-workflow-4',
|
||||
label: 'Bulk Test Workflow #04 - Fourth in Series',
|
||||
type: 'workflow',
|
||||
},
|
||||
{
|
||||
id: 'many-workflow-5',
|
||||
label: 'Bulk Test Workflow #05 - Fifth in Series',
|
||||
type: 'workflow',
|
||||
},
|
||||
{
|
||||
id: 'many-workflow-6',
|
||||
label: 'Bulk Test Workflow #06 - Sixth in Series',
|
||||
type: 'workflow',
|
||||
},
|
||||
{
|
||||
id: 'many-workflow-7',
|
||||
label: 'Bulk Test Workflow #07 - Seventh in Series',
|
||||
type: 'workflow',
|
||||
},
|
||||
{
|
||||
id: 'many-workflow-8',
|
||||
label: 'Bulk Test Workflow #08 - Eighth in Series',
|
||||
type: 'workflow',
|
||||
},
|
||||
{
|
||||
id: 'many-workflow-9',
|
||||
label: 'Bulk Test Workflow #09 - Ninth in Series',
|
||||
type: 'workflow',
|
||||
},
|
||||
{
|
||||
id: 'many-workflow-10',
|
||||
label: 'Bulk Test Workflow #10 - Tenth in Series',
|
||||
type: 'workflow',
|
||||
},
|
||||
{
|
||||
id: 'many-subfolder-1',
|
||||
label: 'Subfolder Within Many Items - Testing Mixed Content',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: 'nested-many-1',
|
||||
label: 'Nested Within Many - Workflow A',
|
||||
type: 'workflow',
|
||||
},
|
||||
{
|
||||
id: 'nested-many-2',
|
||||
label: 'Nested Within Many - Workflow B',
|
||||
type: 'workflow',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'many-workflow-11',
|
||||
label: 'Bulk Test Workflow #11 - Eleventh in Series',
|
||||
type: 'workflow',
|
||||
},
|
||||
{
|
||||
id: 'many-workflow-12',
|
||||
label: 'Bulk Test Workflow #12 - Final in Series',
|
||||
type: 'workflow',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'edge-cases-folder',
|
||||
label: 'Edge Cases Testing',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: 'empty-folder-test-1',
|
||||
label: 'Empty Folder Test #1',
|
||||
type: 'folder',
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
id: 'empty-folder-test-2',
|
||||
label: 'Empty Folder Test #2 with Longer Name for UI Testing',
|
||||
type: 'folder',
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
id: 'single-item-folder',
|
||||
label: 'Single Item Folder',
|
||||
type: 'folder',
|
||||
children: [
|
||||
{
|
||||
id: 'single-workflow',
|
||||
label: 'Lonely Workflow in Single Item Folder',
|
||||
type: 'workflow',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
] as TreeItemType[],
|
||||
},
|
||||
];
|
||||
|
||||
const Template: StoryFn = (args, { argTypes }) => ({
|
||||
setup: () => ({ args }),
|
||||
props: Object.keys(argTypes),
|
||||
components: {
|
||||
Sidebar,
|
||||
},
|
||||
template: `
|
||||
<div class="story">
|
||||
<Sidebar v-bind="args" />
|
||||
</div>
|
||||
`,
|
||||
});
|
||||
|
||||
export const primary = Template.bind({});
|
||||
primary.args = {
|
||||
personal: mockPersonalItems,
|
||||
shared: mockSharedItems,
|
||||
projects: mockProjects,
|
||||
};
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
<script lang="ts" setup>
|
||||
import { TreeItemType } from '.';
|
||||
import N8nIcon from '../N8nIcon';
|
||||
import N8nText from '../N8nText';
|
||||
import SidebarSection from './SidebarSection.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
personal: TreeItemType[];
|
||||
shared: TreeItemType[];
|
||||
projects: { title: string; id: string; icon: 'string'; items: TreeItemType[] }[];
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<nav class="sidebar">
|
||||
<header class="sidebarItem">
|
||||
<div class="icon">
|
||||
<N8nIcon icon="house" />
|
||||
</div>
|
||||
<N8nText>Overview</N8nText>
|
||||
</header>
|
||||
<SidebarSection
|
||||
title="Personal"
|
||||
id="personal"
|
||||
icon="user"
|
||||
:items="props.personal"
|
||||
:selectable="false"
|
||||
:collapsible="false"
|
||||
/>
|
||||
|
||||
<SidebarSection
|
||||
title="Shared with me"
|
||||
id="shared"
|
||||
icon="share"
|
||||
:items="props.shared"
|
||||
:selectable="false"
|
||||
:collapsible="false"
|
||||
/>
|
||||
<N8nText size="small" bold color="text-light" class="sidebarSubheader">Projects</N8nText>
|
||||
<SidebarSection
|
||||
v-for="project in props.projects"
|
||||
:title="project.title"
|
||||
:id="project.id"
|
||||
:key="project.id"
|
||||
:items="project.items"
|
||||
:selectable="false"
|
||||
:collapsible="false"
|
||||
/>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.sidebarItem {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.sidebarItem .icon {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.sidebarSubheader {
|
||||
margin: 16px 0 8px;
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,162 @@
|
|||
<script lang="ts" setup>
|
||||
import { TreeRoot, TreeItem, TreeItemToggleEvent } from 'reka-ui';
|
||||
import type { TreeItemType } from '.';
|
||||
import { ref } from 'vue';
|
||||
import { N8nIcon, N8nIconButton, N8nText } from '..';
|
||||
import { IconName } from '../N8nIcon/icons';
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
id: string;
|
||||
icon?: IconName;
|
||||
items: TreeItemType[];
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
const open = ref<string[]>([]);
|
||||
|
||||
function toggleSection(id: string) {
|
||||
if (open.value.includes(id)) {
|
||||
open.value.splice(open.value.indexOf(id), 1);
|
||||
} else {
|
||||
open.value.push(id);
|
||||
}
|
||||
}
|
||||
|
||||
function preventDefault<T>(event: TreeItemToggleEvent<T>) {
|
||||
if (event.detail.originalEvent.type === 'click') {
|
||||
event.detail.originalEvent.preventDefault();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<header class="itemHeader">
|
||||
<div class="dropdownButton">
|
||||
<div class="icon">
|
||||
<N8nIcon :icon="icon ?? 'layers'" />
|
||||
</div>
|
||||
<N8nIconButton
|
||||
class="button"
|
||||
square
|
||||
type="tertiary"
|
||||
text
|
||||
size="small"
|
||||
:icon="open.includes(id) ? 'chevron-down' : 'chevron-right'"
|
||||
@click="toggleSection(id)"
|
||||
:aria-label="`Toggle ${title} section`"
|
||||
/>
|
||||
</div>
|
||||
<N8nText>{{ title }}</N8nText>
|
||||
</header>
|
||||
<div class="items">
|
||||
<TreeRoot
|
||||
v-if="open.includes(id)"
|
||||
:items="props.items"
|
||||
:get-key="(item: TreeItemType) => item.id"
|
||||
v-slot="{ flattenItems }"
|
||||
>
|
||||
<TreeItem
|
||||
v-for="item in flattenItems"
|
||||
:key="item._id"
|
||||
v-bind="item.bind"
|
||||
v-slot="{ isExpanded, handleToggle }"
|
||||
@toggle="preventDefault"
|
||||
@select="preventDefault"
|
||||
class="item"
|
||||
>
|
||||
<span class="itemIdent" v-for="level in new Array(item.level - 1)" :key="level" />
|
||||
<div class="itemHeader">
|
||||
<div class="dropdownButton" v-if="item.value.type === 'folder'">
|
||||
<div class="icon">
|
||||
<N8nIcon :icon="isExpanded ? 'folder-open' : 'folder'" />
|
||||
</div>
|
||||
<N8nIconButton
|
||||
class="button"
|
||||
square
|
||||
type="tertiary"
|
||||
text
|
||||
size="small"
|
||||
:icon="isExpanded ? 'chevron-down' : 'chevron-right'"
|
||||
@click.stop="handleToggle"
|
||||
:aria-label="`Toggle ${item.value.label} item`"
|
||||
/>
|
||||
</div>
|
||||
<N8nText>{{ item.value.label }}</N8nText>
|
||||
</div>
|
||||
</TreeItem>
|
||||
</TreeRoot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.items {
|
||||
display: flex;
|
||||
padding-left: 8px;
|
||||
margin-left: 16px;
|
||||
border-left: 1px solid var(--color-foreground-light);
|
||||
}
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.itemIdent {
|
||||
display: block;
|
||||
position: relative;
|
||||
width: 8px;
|
||||
align-self: stretch;
|
||||
margin-left: 16px;
|
||||
border-left: 1px solid var(--color-foreground-light);
|
||||
}
|
||||
|
||||
.itemIdent::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -1px;
|
||||
left: -1px;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
background-color: var(--color-foreground-light);
|
||||
}
|
||||
|
||||
.itemHeader {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.dropdownButton {
|
||||
position: relative;
|
||||
border-radius: var(--border-radius-small);
|
||||
|
||||
.icon {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.button {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&:hover .button,
|
||||
.button:focus {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&:hover .icon,
|
||||
&:focus-within .icon {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
export type TreeItemType = FolderItem | WorkflowItem;
|
||||
|
||||
export interface BaseItem {
|
||||
id: string;
|
||||
label: string;
|
||||
disabled?: boolean;
|
||||
data?: any;
|
||||
}
|
||||
|
||||
export interface FolderItem extends BaseItem {
|
||||
type: 'folder';
|
||||
children?: TreeItemType[];
|
||||
}
|
||||
|
||||
export interface WorkflowItem extends BaseItem {
|
||||
type: 'workflow';
|
||||
}
|
||||
|
|
@ -525,133 +525,3 @@ onClickOutside(createBtn as Ref<VueInstance>, () => {
|
|||
</N8nMenu>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" module>
|
||||
.sideMenu {
|
||||
display: grid;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
grid-template-rows: auto 1fr auto;
|
||||
border-right: var(--border-width-base) var(--border-style-base) var(--color-foreground-base);
|
||||
transition: width 150ms ease-in-out;
|
||||
width: $sidebar-expanded-width;
|
||||
background-color: var(--menu-background, var(--color-background-xlight));
|
||||
|
||||
.logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: var(--spacing-xs);
|
||||
justify-content: space-between;
|
||||
|
||||
img {
|
||||
position: relative;
|
||||
left: 1px;
|
||||
height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
&.sideMenuCollapsed {
|
||||
width: $sidebar-width;
|
||||
min-width: auto;
|
||||
|
||||
.logo {
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sideMenuCollapseButton {
|
||||
position: absolute;
|
||||
right: -10px;
|
||||
top: 50%;
|
||||
z-index: 999;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: var(--color-text-base);
|
||||
background-color: var(--color-foreground-xlight);
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: var(--border-width-base) var(--border-style-base) var(--color-foreground-base);
|
||||
border-radius: 50%;
|
||||
|
||||
&:hover {
|
||||
color: var(--color-primary-shade-1);
|
||||
}
|
||||
}
|
||||
|
||||
.updates {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
padding: var(--spacing-2xs) var(--spacing-l);
|
||||
margin: var(--spacing-2xs) 0 0;
|
||||
|
||||
svg {
|
||||
color: var(--color-text-base) !important;
|
||||
}
|
||||
span {
|
||||
display: none;
|
||||
&.expanded {
|
||||
display: initial;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
&,
|
||||
& svg {
|
||||
color: var(--color-text-dark) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.userArea {
|
||||
display: flex;
|
||||
padding: var(--spacing-xs);
|
||||
align-items: center;
|
||||
height: 60px;
|
||||
border-top: var(--border-width-base) var(--border-style-base) var(--color-foreground-base);
|
||||
|
||||
.userName {
|
||||
display: none;
|
||||
overflow: hidden;
|
||||
width: 100px;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
&.expanded {
|
||||
display: initial;
|
||||
}
|
||||
|
||||
span {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
.userActions {
|
||||
display: none;
|
||||
|
||||
&.expanded {
|
||||
display: initial;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-height: 470px) {
|
||||
:global(#help) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.readOnlyEnvironmentIcon {
|
||||
display: inline-block;
|
||||
color: white;
|
||||
background-color: var(--color-warning);
|
||||
align-self: center;
|
||||
padding: 2px;
|
||||
border-radius: var(--border-radius-small);
|
||||
margin: 7px 12px 0 5px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user