From 97d2550c13df26f72a93c7b58ca64ca50bc611f5 Mon Sep 17 00:00:00 2001 From: AAsteria <2329152202@qq.com> Date: Sat, 21 Dec 2024 10:28:04 +0800 Subject: [PATCH 1/6] [Feat] dashboard (need refactoring) --- src/workbench/parts/workspace/workspace.ts | 16 ++- .../services/dashboard/dashboardSlider.ts | 41 ++++++ .../services/dashboard/dashboardSubView.ts | 88 ++++++++++++ .../services/dashboard/dashboardView.ts | 128 ++++++++++++++++++ 4 files changed, 272 insertions(+), 1 deletion(-) create mode 100644 src/workbench/services/dashboard/dashboardSlider.ts create mode 100644 src/workbench/services/dashboard/dashboardSubView.ts create mode 100644 src/workbench/services/dashboard/dashboardView.ts diff --git a/src/workbench/parts/workspace/workspace.ts b/src/workbench/parts/workspace/workspace.ts index 67ba5c062..4c36afa88 100644 --- a/src/workbench/parts/workspace/workspace.ts +++ b/src/workbench/parts/workspace/workspace.ts @@ -6,6 +6,8 @@ import { IInstantiationService } from "src/platform/instantiation/common/instant import { IEditorService } from "src/workbench/parts/workspace/editor/editorService"; import { OPERATING_SYSTEM, Platform } from 'src/base/common/platform'; import { Orientation } from 'src/base/browser/basic/dom'; +import { Priority } from 'src/base/common/event'; +import { DashboardView } from 'src/workbench/services/dashboard/dashboardView'; export const IWorkspaceService = createService('workspace-service'); @@ -52,8 +54,20 @@ export class WorkspaceComponent extends Component implements IWorkspaceService { }); } + // layout.push({ + // component: this.editorService, + // initSize: null, + // maximumSize: null, + // minimumSize: null, + // }); + const dashboardView = this.instantiationService.createInstance(DashboardView, { + id: 'workspace-dashboard', + priority: Priority.Low, + content: ["Pinned Notes", "Recent Items", "What's New"], + }); + layout.push({ - component: this.editorService, + component: dashboardView, initSize: null, maximumSize: null, minimumSize: null, diff --git a/src/workbench/services/dashboard/dashboardSlider.ts b/src/workbench/services/dashboard/dashboardSlider.ts new file mode 100644 index 000000000..587f92fb8 --- /dev/null +++ b/src/workbench/services/dashboard/dashboardSlider.ts @@ -0,0 +1,41 @@ +import { IInstantiationService } from "src/platform/instantiation/common/instantiation"; +import { Component } from "src/workbench/services/component/component"; + +export class DashboardSlider extends Component { + + private items: HTMLElement[]; + + constructor( + @IInstantiationService instantiationService: IInstantiationService, + items: HTMLElement[], + ) { + super('dashboard-slider', null, instantiationService); + this.items = items; + } + + public createView(): HTMLElement { + const sliderContainer = document.createElement('div'); + sliderContainer.classList.add('slider'); + + this.items.forEach(item => { + const itemContainer = document.createElement('div'); + itemContainer.classList.add('slider-item'); + itemContainer.appendChild(item); + sliderContainer.appendChild(itemContainer); + }); + + // Add smooth scrolling + sliderContainer.style.scrollBehavior = 'smooth'; + + return sliderContainer; + } + + protected override _createContent(): void { + const viewElement = this.createView(); + this.element.appendChild(viewElement); + } + + protected override _registerListeners(): void { + // Additional listeners (if needed) + } +} diff --git a/src/workbench/services/dashboard/dashboardSubView.ts b/src/workbench/services/dashboard/dashboardSubView.ts new file mode 100644 index 000000000..3fa8b5b5c --- /dev/null +++ b/src/workbench/services/dashboard/dashboardSubView.ts @@ -0,0 +1,88 @@ +import { DashboardSlider } from "./dashboardSlider"; +import { Component } from "src/workbench/services/component/component"; +import { IInstantiationService } from "src/platform/instantiation/common/instantiation"; +import { IDashboardViewOpts } from "src/workbench/services/dashboard/dashboardView"; + +export class DashboardSubView extends Component { + private slider: DashboardSlider; + private opts: IDashboardViewOpts; + + constructor( + @IInstantiationService instantiationService: IInstantiationService, + opts: IDashboardViewOpts + ) { + super("navigation-view", null, instantiationService); + this.opts = opts; + this.slider = new DashboardSlider( + instantiationService, + this.createSliderItems(opts.content || []) + ); + } + + public render(): HTMLElement { + const subViewContainer = document.createElement("div"); + subViewContainer.classList.add("dashboard-subview"); + subViewContainer.setAttribute("data-id", this.opts.id); + + // Create section header + const header = document.createElement("div"); + header.classList.add("section-header"); + + // Append title and dropdown to the header + const title = this.__createSubViewTitle(this.opts.title || "Default Title"); + const sortDropdown = this.__createSortDropdown(); + + header.appendChild(title); + header.appendChild(sortDropdown); + + // Append header to container + subViewContainer.appendChild(header); + + // Add slider content + const sliderElement = this.slider.createView(); + subViewContainer.appendChild(sliderElement); + + return subViewContainer; + } + + protected override _createContent(): void { + this.render(); + } + + protected override _registerListeners(): void { + // Add event listeners here if necessary + } + + private __createSortDropdown(): HTMLElement { + const sortDropdown = document.createElement("div"); + sortDropdown.classList.add("sort-dropdown"); + + // Add triangle icon + const triangleIcon = document.createElement("div"); + triangleIcon.classList.add("triangle-icon"); + sortDropdown.appendChild(triangleIcon); + + // Add text + const dropdownText = document.createElement("div"); + dropdownText.classList.add("dropdown-text"); + dropdownText.textContent = "Last modified"; + sortDropdown.appendChild(dropdownText); + + return sortDropdown; + } + + private __createSubViewTitle(titleText: string): HTMLElement { + const title = document.createElement("h2"); + title.textContent = titleText; + return title; + } + + private createSliderItems(content: string[]): HTMLElement[] { + return content.map((itemText) => { + const itemElement = document.createElement("div"); + itemElement.textContent = itemText; + itemElement.classList.add("slider-item"); + return itemElement; + }); + } +} diff --git a/src/workbench/services/dashboard/dashboardView.ts b/src/workbench/services/dashboard/dashboardView.ts new file mode 100644 index 000000000..af46b25a0 --- /dev/null +++ b/src/workbench/services/dashboard/dashboardView.ts @@ -0,0 +1,128 @@ +import "src/workbench/services/dashboard/media/dashboard.scss"; +import { Priority } from "src/base/common/event"; +import { IInstantiationService } from "src/platform/instantiation/common/instantiation"; +import { Component } from "src/workbench/services/component/component"; +import { DashboardSubView } from "src/workbench/services/dashboard/dashboardSubView"; + +export interface IDashboardViewOpts { + + /** + * The unique identifier of the dashboard view. + */ + id: string; + + /** + * When adding/removing view, the view with higher priority will show at top + * first. + * @default Priority.Low + */ + priority?: Priority; + + /** + * An array of content items (e.g., strings or identifiers) to be displayed within the view. + * Each item represents a sinågle content block to be rendered in the dashboard subview. + * @optional + */ + content?: string[]; + + /** + * The title for a dashboard view (especially used in subviews) + */ + title?: string; +} + +export class DashboardView extends Component { + + // [fields] + + private subViews: DashboardSubView[] = []; + + // [constructor] + + constructor( + opts: IDashboardViewOpts, + @IInstantiationService instantiationService: IInstantiationService + ) { + super('dashboard-view', null, instantiationService); + } + + // [public methods] + + public createView(): HTMLElement { + const container = document.createElement("div"); + container.classList.add("dashboard-view"); + + // Create SubViews for each section + const sections: IDashboardViewOpts[] = [ + { + id: "welcome-section", + priority: Priority.High, + title: "Hello," + }, + { + id: "pinned-notes", + priority: Priority.High, + title: "Pinned", + content: this.generatePlaceholderItems("Pinned"), + }, + { + id: "recent-items", + priority: Priority.Normal, + title: "Recent", + content: this.generatePlaceholderItems("Recent"), + }, + { + id: "new-features", + priority: Priority.Low, + title: "New Features", + content: this.generatePlaceholderItems("New Features"), + }, + ]; + + sections.forEach((sectionOpts) => { + if (sectionOpts.id === "welcome-section") { + // TODO: prepare some welcome sentences + const welcomeSection = this.createWelcomeSection(sectionOpts.title || "Hello, user!"); + container.appendChild(welcomeSection); + } else { + // Create other subviews + const subView = new DashboardSubView( + this.instantiationService, + sectionOpts + ); + container.appendChild(subView.render()); // Append the rendered subview to the container + this.subViews.push(subView); // Store for future use + } + }); + + return container; + } + + protected override _createContent(): void { + const viewElement = this.createView(); + this.element.appendChild(viewElement); + } + + protected override _registerListeners(): void { + + } + + // [private methods] + private createWelcomeSection(title: string): HTMLElement { + const welcomeSection = document.createElement("div"); + welcomeSection.classList.add("welcome-section"); + welcomeSection.innerHTML = ` +

${title}

+ + `; + return welcomeSection; + } + + private generatePlaceholderItems(sectionId: string): string[] { + const items: string[] = []; + for (let i = 1; i <= 10; i++) { + items.push(`${sectionId} Item ${i}`); + } + return items; + } +} From 45f2dc95b9f3c320c29222ee90762cc9d434c8a6 Mon Sep 17 00:00:00 2001 From: AAsteria <2329152202@qq.com> Date: Sat, 21 Dec 2024 10:28:24 +0800 Subject: [PATCH 2/6] [Feat] add dashboard styles --- .../services/dashboard/media/dashboard.scss | 123 ++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 src/workbench/services/dashboard/media/dashboard.scss diff --git a/src/workbench/services/dashboard/media/dashboard.scss b/src/workbench/services/dashboard/media/dashboard.scss new file mode 100644 index 000000000..c42702256 --- /dev/null +++ b/src/workbench/services/dashboard/media/dashboard.scss @@ -0,0 +1,123 @@ +.dashboard-view { + display: flex; + flex-direction: column; + padding: 100px 65px; + height: 100vh; + overflow-y: auto; + + .welcome-section { + display: flex; + flex-direction: column; + align-items: flex-start; + + h1 { + font-size: 28px; + font-weight: bolder; + color: #211F27; + margin-bottom: 24px; + } + + .new-note-btn { + background-color: #2aa882; + color: #ffffff; + border: none; + border-radius: 2px; + padding: 8px 10px; + margin-bottom: 8px; + cursor: pointer; + font-size: 14px; + transition: background-color 0.3s ease; + + &:hover { + background-color: #249372; + } + } + } + + .dashboard-subview { + padding: 1rem 0; + + .section-header { + display: flex; + align-items: center; + margin-bottom: 16px; + + h2 { + font-size: 1.2rem; + font-weight: 900; + color: #211F27; + margin: 0; + } + + .sort-dropdown { + display: flex; + align-items: center; + font-size: 12px; + color: #5A564D; + cursor: pointer; + background-color: #F0F8F5; + border: 2px; + margin-left: 8px; + padding: 4px 8px; + border-radius: 2px; + + .triangle-icon { + width: 4px; + height: 6px; + background-color: #2aa882; + clip-path: polygon(0% 0%, 100% 50%, 0% 100%); + margin-right: 6px; + } + + .dropdown-text { + font-weight: 500; + color: #5A564D; + } + } + } + + .slider { + display: flex; + overflow-x: auto; + gap: 1rem; + padding-bottom: 1rem; // Add padding for smooth scrolling + + scrollbar-color: #D9D9D9 transparent; + + &::-webkit-scrollbar { + height: 3.2px; + } + + &::-webkit-scrollbar-thumb { + background: #D9D9D9; + } + + .slider-item { + flex: 0 0 calc((100% - 3rem) / 5); + background-color: transparent; + border: 1px solid #e6e6e6; + padding: 10px; + text-align: left; + font-size: 10px; + font-weight: 500; + color: #495057; + transition: transform 0.2s ease; + + &:hover { + cursor: pointer; + } + + .item-title { + font-weight: bold; + color: #211F27; + margin-bottom: 0.5rem; + } + + .item-meta { + font-size: 0.8rem; + color: #5A564D; + } + } + } + } +} From 0ab1df826893899eddfc2d0c2d789c8d3ad88ba4 Mon Sep 17 00:00:00 2001 From: AAsteria <2329152202@qq.com> Date: Sun, 22 Dec 2024 09:55:21 +0800 Subject: [PATCH 3/6] [Refactor] composite pattern to for extensible subview creating process --- src/workbench/parts/workspace/workspace.ts | 3 +- .../services/dashboard/dashboardSlider.ts | 6 +- .../services/dashboard/dashboardSubView.ts | 52 +++----- .../services/dashboard/dashboardView.ts | 122 ++++++------------ .../services/dashboard/media/dashboard.scss | 11 +- .../services/dashboard/type1SubView.ts | 55 ++++++++ .../services/dashboard/type2SubView.ts | 73 +++++++++++ 7 files changed, 198 insertions(+), 124 deletions(-) create mode 100644 src/workbench/services/dashboard/type1SubView.ts create mode 100644 src/workbench/services/dashboard/type2SubView.ts diff --git a/src/workbench/parts/workspace/workspace.ts b/src/workbench/parts/workspace/workspace.ts index 4c36afa88..5a1d9fdcf 100644 --- a/src/workbench/parts/workspace/workspace.ts +++ b/src/workbench/parts/workspace/workspace.ts @@ -8,6 +8,7 @@ import { OPERATING_SYSTEM, Platform } from 'src/base/common/platform'; import { Orientation } from 'src/base/browser/basic/dom'; import { Priority } from 'src/base/common/event'; import { DashboardView } from 'src/workbench/services/dashboard/dashboardView'; +import { Type1SubView } from 'src/workbench/services/dashboard/type1SubView'; export const IWorkspaceService = createService('workspace-service'); @@ -62,8 +63,6 @@ export class WorkspaceComponent extends Component implements IWorkspaceService { // }); const dashboardView = this.instantiationService.createInstance(DashboardView, { id: 'workspace-dashboard', - priority: Priority.Low, - content: ["Pinned Notes", "Recent Items", "What's New"], }); layout.push({ diff --git a/src/workbench/services/dashboard/dashboardSlider.ts b/src/workbench/services/dashboard/dashboardSlider.ts index 587f92fb8..29800fcd2 100644 --- a/src/workbench/services/dashboard/dashboardSlider.ts +++ b/src/workbench/services/dashboard/dashboardSlider.ts @@ -23,10 +23,6 @@ export class DashboardSlider extends Component { itemContainer.appendChild(item); sliderContainer.appendChild(itemContainer); }); - - // Add smooth scrolling - sliderContainer.style.scrollBehavior = 'smooth'; - return sliderContainer; } @@ -36,6 +32,6 @@ export class DashboardSlider extends Component { } protected override _registerListeners(): void { - // Additional listeners (if needed) + } } diff --git a/src/workbench/services/dashboard/dashboardSubView.ts b/src/workbench/services/dashboard/dashboardSubView.ts index 3fa8b5b5c..db890955f 100644 --- a/src/workbench/services/dashboard/dashboardSubView.ts +++ b/src/workbench/services/dashboard/dashboardSubView.ts @@ -1,17 +1,28 @@ import { DashboardSlider } from "./dashboardSlider"; import { Component } from "src/workbench/services/component/component"; import { IInstantiationService } from "src/platform/instantiation/common/instantiation"; -import { IDashboardViewOpts } from "src/workbench/services/dashboard/dashboardView"; -export class DashboardSubView extends Component { +export interface IDashboardSubView { + readonly id: string; + render(parentElement: HTMLElement): HTMLElement; + dispose(): void; +} + +export interface IDashboardSubViewOpts { + id: string; + title?: string; + content?: string[] +} + +export class DashboardSubView extends Component implements IDashboardSubView { private slider: DashboardSlider; - private opts: IDashboardViewOpts; + private opts: IDashboardSubViewOpts; constructor( @IInstantiationService instantiationService: IInstantiationService, - opts: IDashboardViewOpts + opts: IDashboardSubViewOpts ) { - super("navigation-view", null, instantiationService); + super("dashboard-subview", null, instantiationService); this.opts = opts; this.slider = new DashboardSlider( instantiationService, @@ -19,7 +30,7 @@ export class DashboardSubView extends Component { ); } - public render(): HTMLElement { + public render(parentElement: HTMLElement): HTMLElement { const subViewContainer = document.createElement("div"); subViewContainer.classList.add("dashboard-subview"); subViewContainer.setAttribute("data-id", this.opts.id); @@ -28,47 +39,24 @@ export class DashboardSubView extends Component { const header = document.createElement("div"); header.classList.add("section-header"); - // Append title and dropdown to the header const title = this.__createSubViewTitle(this.opts.title || "Default Title"); - const sortDropdown = this.__createSortDropdown(); - header.appendChild(title); - header.appendChild(sortDropdown); - - // Append header to container subViewContainer.appendChild(header); // Add slider content const sliderElement = this.slider.createView(); subViewContainer.appendChild(sliderElement); + parentElement.appendChild(subViewContainer); return subViewContainer; } protected override _createContent(): void { - this.render(); + // Content is already handled in the render method } protected override _registerListeners(): void { - // Add event listeners here if necessary - } - - private __createSortDropdown(): HTMLElement { - const sortDropdown = document.createElement("div"); - sortDropdown.classList.add("sort-dropdown"); - - // Add triangle icon - const triangleIcon = document.createElement("div"); - triangleIcon.classList.add("triangle-icon"); - sortDropdown.appendChild(triangleIcon); - - // Add text - const dropdownText = document.createElement("div"); - dropdownText.classList.add("dropdown-text"); - dropdownText.textContent = "Last modified"; - sortDropdown.appendChild(dropdownText); - - return sortDropdown; + // Register event listeners if needed } private __createSubViewTitle(titleText: string): HTMLElement { diff --git a/src/workbench/services/dashboard/dashboardView.ts b/src/workbench/services/dashboard/dashboardView.ts index af46b25a0..15297e2c0 100644 --- a/src/workbench/services/dashboard/dashboardView.ts +++ b/src/workbench/services/dashboard/dashboardView.ts @@ -2,10 +2,11 @@ import "src/workbench/services/dashboard/media/dashboard.scss"; import { Priority } from "src/base/common/event"; import { IInstantiationService } from "src/platform/instantiation/common/instantiation"; import { Component } from "src/workbench/services/component/component"; -import { DashboardSubView } from "src/workbench/services/dashboard/dashboardSubView"; +import { Type1SubView } from "src/workbench/services/dashboard/type1SubView"; +import { Type2SubView } from "src/workbench/services/dashboard/type2SubView"; +import { IDashboardSubView } from "src/workbench/services/dashboard/dashboardSubView"; export interface IDashboardViewOpts { - /** * The unique identifier of the dashboard view. */ @@ -18,81 +19,28 @@ export interface IDashboardViewOpts { */ priority?: Priority; - /** - * An array of content items (e.g., strings or identifiers) to be displayed within the view. - * Each item represents a sinågle content block to be rendered in the dashboard subview. - * @optional - */ - content?: string[]; - - /** - * The title for a dashboard view (especially used in subviews) - */ - title?: string; + subViews?: IDashboardSubView[]; } export class DashboardView extends Component { - - // [fields] - - private subViews: DashboardSubView[] = []; - - // [constructor] + private subViews: IDashboardSubView[] = []; constructor( - opts: IDashboardViewOpts, + private opts: IDashboardViewOpts, @IInstantiationService instantiationService: IInstantiationService ) { - super('dashboard-view', null, instantiationService); + super("dashboard-view", null, instantiationService); } - // [public methods] - public createView(): HTMLElement { const container = document.createElement("div"); container.classList.add("dashboard-view"); // Create SubViews for each section - const sections: IDashboardViewOpts[] = [ - { - id: "welcome-section", - priority: Priority.High, - title: "Hello," - }, - { - id: "pinned-notes", - priority: Priority.High, - title: "Pinned", - content: this.generatePlaceholderItems("Pinned"), - }, - { - id: "recent-items", - priority: Priority.Normal, - title: "Recent", - content: this.generatePlaceholderItems("Recent"), - }, - { - id: "new-features", - priority: Priority.Low, - title: "New Features", - content: this.generatePlaceholderItems("New Features"), - }, - ]; - - sections.forEach((sectionOpts) => { - if (sectionOpts.id === "welcome-section") { - // TODO: prepare some welcome sentences - const welcomeSection = this.createWelcomeSection(sectionOpts.title || "Hello, user!"); - container.appendChild(welcomeSection); - } else { - // Create other subviews - const subView = new DashboardSubView( - this.instantiationService, - sectionOpts - ); - container.appendChild(subView.render()); // Append the rendered subview to the container - this.subViews.push(subView); // Store for future use - } + this.subViews = this.createSubViews(); + + this.subViews.forEach((subView) => { + container.appendChild(subView.render(container)); // Render and append subviews }); return container; @@ -104,25 +52,37 @@ export class DashboardView extends Component { } protected override _registerListeners(): void { - - } - - // [private methods] - private createWelcomeSection(title: string): HTMLElement { - const welcomeSection = document.createElement("div"); - welcomeSection.classList.add("welcome-section"); - welcomeSection.innerHTML = ` -

${title}

- - `; - return welcomeSection; + // No listeners required in this class } - private generatePlaceholderItems(sectionId: string): string[] { - const items: string[] = []; - for (let i = 1; i <= 10; i++) { - items.push(`${sectionId} Item ${i}`); - } - return items; + private createSubViews(): IDashboardSubView[] { + const subViews: IDashboardSubView[] = []; + + // Add the welcome section (Type1) + subViews.push(new Type1SubView({ + id: 'type1', + title: 'Welcome to the Dashboard' + })); + + // Add other sections (Type2) + subViews.push(new Type2SubView({ + id: 'type2', + title: 'Pinned Notes', + content: ["Pinned Note 1", "Pinned Note 2"] + })); + + subViews.push(new Type2SubView({ + id: 'type3', + title: 'Recent Items', + content: ["Recent Item 1", "Recent Item 2"] + })); + + subViews.push(new Type2SubView({ + id: 'type4', + title: "What's New", + content: ["New Feature 1", "New Feature 2"] + })); + + return subViews; } } diff --git a/src/workbench/services/dashboard/media/dashboard.scss b/src/workbench/services/dashboard/media/dashboard.scss index c42702256..806634632 100644 --- a/src/workbench/services/dashboard/media/dashboard.scss +++ b/src/workbench/services/dashboard/media/dashboard.scss @@ -5,12 +5,13 @@ height: 100vh; overflow-y: auto; - .welcome-section { + // Targeting type1-subview for the welcome section + .type1-subview { display: flex; flex-direction: column; align-items: flex-start; - h1 { + h2 { font-size: 28px; font-weight: bolder; color: #211F27; @@ -34,7 +35,8 @@ } } - .dashboard-subview { + // Updated for all subviews + .type2-subview { padding: 1rem 0; .section-header { @@ -76,7 +78,7 @@ } } - .slider { + .content-container { display: flex; overflow-x: auto; gap: 1rem; @@ -102,6 +104,7 @@ font-weight: 500; color: #495057; transition: transform 0.2s ease; + scroll-behavior: smooth; &:hover { cursor: pointer; diff --git a/src/workbench/services/dashboard/type1SubView.ts b/src/workbench/services/dashboard/type1SubView.ts new file mode 100644 index 000000000..dbfbb0c76 --- /dev/null +++ b/src/workbench/services/dashboard/type1SubView.ts @@ -0,0 +1,55 @@ +import { IDashboardSubView, IDashboardSubViewOpts } from "src/workbench/services/dashboard/dashboardSubView"; + +export class Type1SubView implements IDashboardSubView { + + // [fields] + + public id: string = 'type1'; + + // [constructor] + + constructor( + private opts: IDashboardSubViewOpts + ) {} + + // [public methods] + + public render(): HTMLElement { + const subViewContainer = document.createElement("div"); + subViewContainer.classList.add("type1-subview"); + subViewContainer.setAttribute("data-id", this.opts.id); + + // Create section header + const header = document.createElement("div"); + header.classList.add("section-header"); + + // Title for the section + const title = document.createElement("h2"); + title.textContent = this.opts.title || "Welcome to the Dashboard"; + header.appendChild(title); + + subViewContainer.appendChild(header); + + // Add the New Note button + const newNoteButton = this.__createNewNoteButton(); + subViewContainer.appendChild(newNoteButton); + + return subViewContainer; + } + + public dispose(): void { + + } + + // [private methods] + + private __createNewNoteButton(): HTMLElement { + const button = document.createElement("button"); + button.classList.add("new-note-btn"); + button.textContent = "New Note"; + button.addEventListener("click", () => { + console.log("New Note button clicked"); + }); + return button; + } +} diff --git a/src/workbench/services/dashboard/type2SubView.ts b/src/workbench/services/dashboard/type2SubView.ts new file mode 100644 index 000000000..7585ca6d7 --- /dev/null +++ b/src/workbench/services/dashboard/type2SubView.ts @@ -0,0 +1,73 @@ +import { IDashboardSubView, IDashboardSubViewOpts } from "src/workbench/services/dashboard/dashboardSubView"; + +export class Type2SubView implements IDashboardSubView { + + // [fields] + + public id: string = 'type2'; + + // [constructor] + + constructor( + private opts: IDashboardSubViewOpts + ) {} + + // [public methods] + + public render(): HTMLElement { + const subViewContainer = document.createElement("div"); + subViewContainer.classList.add("type2-subview"); + subViewContainer.setAttribute("data-id", this.opts.id); + + const header = document.createElement("div"); + header.classList.add("section-header"); + + // Title for the section + const title = document.createElement("h2"); + title.textContent = this.opts.title || "Default Title"; + header.appendChild(title); + + // Add dropdown to the header + const sortDropdown = this.__createSortDropdown(); + header.appendChild(sortDropdown); + + subViewContainer.appendChild(header); + + // Create the content items + const contentContainer = document.createElement("div"); + contentContainer.classList.add("content-container"); + this.opts.content?.forEach(itemText => { + const itemElement = document.createElement("div"); + itemElement.textContent = itemText; + itemElement.classList.add("content-item"); + contentContainer.appendChild(itemElement); + }); + + subViewContainer.appendChild(contentContainer); + return subViewContainer; + } + + public dispose(): void { + + } + + // [private methods] + + private __createSortDropdown(): HTMLElement { + const sortDropdown = document.createElement("div"); + sortDropdown.classList.add("sort-dropdown"); + + // Add triangle icon + const triangleIcon = document.createElement("div"); + triangleIcon.classList.add("triangle-icon"); + sortDropdown.appendChild(triangleIcon); + + // Add text + const dropdownText = document.createElement("div"); + dropdownText.classList.add("dropdown-text"); + dropdownText.textContent = "Last modified"; + sortDropdown.appendChild(dropdownText); + + return sortDropdown; + } +} From 7d4a614e1bf62765d2527d908fbcad75d62993ec Mon Sep 17 00:00:00 2001 From: AAsteria <2329152202@qq.com> Date: Sun, 22 Dec 2024 13:19:39 +0800 Subject: [PATCH 4/6] [Refactor] dynamically create subViews by passing subview options --- src/workbench/parts/workspace/workspace.ts | 6 +- .../services/dashboard/dashboardSubView.ts | 101 +++++++----------- .../services/dashboard/dashboardView.ts | 74 ++++--------- .../services/dashboard/type1SubView.ts | 13 ++- .../services/dashboard/type2SubView.ts | 13 ++- 5 files changed, 84 insertions(+), 123 deletions(-) diff --git a/src/workbench/parts/workspace/workspace.ts b/src/workbench/parts/workspace/workspace.ts index 5a1d9fdcf..2404cd354 100644 --- a/src/workbench/parts/workspace/workspace.ts +++ b/src/workbench/parts/workspace/workspace.ts @@ -62,7 +62,11 @@ export class WorkspaceComponent extends Component implements IWorkspaceService { // minimumSize: null, // }); const dashboardView = this.instantiationService.createInstance(DashboardView, { - id: 'workspace-dashboard', + subViews: [ + { id: 'type1', title: 'Hello,' }, + { id: 'type2', title: 'Pinned', content: ["Note 1", "Note 2"] }, + { id: 'type3', title: 'Recent', content: ["Item 1", "Item 2"] }, + ], }); layout.push({ diff --git a/src/workbench/services/dashboard/dashboardSubView.ts b/src/workbench/services/dashboard/dashboardSubView.ts index db890955f..0ee6facae 100644 --- a/src/workbench/services/dashboard/dashboardSubView.ts +++ b/src/workbench/services/dashboard/dashboardSubView.ts @@ -1,76 +1,51 @@ -import { DashboardSlider } from "./dashboardSlider"; -import { Component } from "src/workbench/services/component/component"; -import { IInstantiationService } from "src/platform/instantiation/common/instantiation"; +import { Priority } from "src/base/common/event"; export interface IDashboardSubView { + /** + * The unique identifier of the subview. + */ readonly id: string; + + /** + * The priority of the subview when determining its display order. + * Subviews with higher priority are displayed before others. + * @default Priority.Low + */ + priority?: Priority; + + /** + * Renders the subview and appends it to the provided parent element. + * @param parentElement - The DOM element to append the rendered subview to. + * @returns The rendered subview element. + */ render(parentElement: HTMLElement): HTMLElement; + + /** + * Cleans up resources and removes the subview when it is no longer needed. + */ dispose(): void; } export interface IDashboardSubViewOpts { + /** + * The unique identifier of the dashboard view. + */ id: string; - title?: string; - content?: string[] -} - -export class DashboardSubView extends Component implements IDashboardSubView { - private slider: DashboardSlider; - private opts: IDashboardSubViewOpts; - - constructor( - @IInstantiationService instantiationService: IInstantiationService, - opts: IDashboardSubViewOpts - ) { - super("dashboard-subview", null, instantiationService); - this.opts = opts; - this.slider = new DashboardSlider( - instantiationService, - this.createSliderItems(opts.content || []) - ); - } - - public render(parentElement: HTMLElement): HTMLElement { - const subViewContainer = document.createElement("div"); - subViewContainer.classList.add("dashboard-subview"); - subViewContainer.setAttribute("data-id", this.opts.id); - // Create section header - const header = document.createElement("div"); - header.classList.add("section-header"); + /** + * When adding/removing subview, the view with higher priority will show at top + * first. + * @default Priority.Low + */ + priority?: Priority; - const title = this.__createSubViewTitle(this.opts.title || "Default Title"); - header.appendChild(title); - subViewContainer.appendChild(header); - - // Add slider content - const sliderElement = this.slider.createView(); - subViewContainer.appendChild(sliderElement); - - parentElement.appendChild(subViewContainer); - return subViewContainer; - } - - protected override _createContent(): void { - // Content is already handled in the render method - } - - protected override _registerListeners(): void { - // Register event listeners if needed - } - - private __createSubViewTitle(titleText: string): HTMLElement { - const title = document.createElement("h2"); - title.textContent = titleText; - return title; - } + /** + * The title of the subview, typically displayed as a header. + */ + title?: string; - private createSliderItems(content: string[]): HTMLElement[] { - return content.map((itemText) => { - const itemElement = document.createElement("div"); - itemElement.textContent = itemText; - itemElement.classList.add("slider-item"); - return itemElement; - }); - } + /** + * The content of the subview, which can include an array of strings. + */ + content?: string[]; } diff --git a/src/workbench/services/dashboard/dashboardView.ts b/src/workbench/services/dashboard/dashboardView.ts index 15297e2c0..4402d8bd6 100644 --- a/src/workbench/services/dashboard/dashboardView.ts +++ b/src/workbench/services/dashboard/dashboardView.ts @@ -1,29 +1,17 @@ import "src/workbench/services/dashboard/media/dashboard.scss"; -import { Priority } from "src/base/common/event"; import { IInstantiationService } from "src/platform/instantiation/common/instantiation"; import { Component } from "src/workbench/services/component/component"; import { Type1SubView } from "src/workbench/services/dashboard/type1SubView"; import { Type2SubView } from "src/workbench/services/dashboard/type2SubView"; -import { IDashboardSubView } from "src/workbench/services/dashboard/dashboardSubView"; +import { IDashboardSubView, IDashboardSubViewOpts } from "src/workbench/services/dashboard/dashboardSubView"; +import { Priority } from "src/base/common/event"; +import { panic } from "src/base/common/utilities/panic"; export interface IDashboardViewOpts { - /** - * The unique identifier of the dashboard view. - */ - id: string; - - /** - * When adding/removing view, the view with higher priority will show at top - * first. - * @default Priority.Low - */ - priority?: Priority; - - subViews?: IDashboardSubView[]; + subViews?: IDashboardSubViewOpts[]; } export class DashboardView extends Component { - private subViews: IDashboardSubView[] = []; constructor( private opts: IDashboardViewOpts, @@ -36,12 +24,11 @@ export class DashboardView extends Component { const container = document.createElement("div"); container.classList.add("dashboard-view"); - // Create SubViews for each section - this.subViews = this.createSubViews(); - - this.subViews.forEach((subView) => { - container.appendChild(subView.render(container)); // Render and append subviews - }); + this.createSubViews() + .sort((a, b) => (a.priority || Priority.Low) - (b.priority || Priority.Low)) + .forEach((subView) => { + container.appendChild(subView.render(container)); + }); return container; } @@ -51,38 +38,19 @@ export class DashboardView extends Component { this.element.appendChild(viewElement); } - protected override _registerListeners(): void { - // No listeners required in this class - } + protected override _registerListeners(): void {} private createSubViews(): IDashboardSubView[] { - const subViews: IDashboardSubView[] = []; - - // Add the welcome section (Type1) - subViews.push(new Type1SubView({ - id: 'type1', - title: 'Welcome to the Dashboard' - })); - - // Add other sections (Type2) - subViews.push(new Type2SubView({ - id: 'type2', - title: 'Pinned Notes', - content: ["Pinned Note 1", "Pinned Note 2"] - })); - - subViews.push(new Type2SubView({ - id: 'type3', - title: 'Recent Items', - content: ["Recent Item 1", "Recent Item 2"] - })); - - subViews.push(new Type2SubView({ - id: 'type4', - title: "What's New", - content: ["New Feature 1", "New Feature 2"] - })); - - return subViews; + const subViewOptions = this.opts.subViews || []; + return subViewOptions.map((opts) => { + switch (opts.id) { + case 'type1': + return new Type1SubView(opts); + case 'type2': + return new Type2SubView(opts); + default: + panic(`Unsupported subview type: ${opts.id}`); + } + }); } } diff --git a/src/workbench/services/dashboard/type1SubView.ts b/src/workbench/services/dashboard/type1SubView.ts index dbfbb0c76..9374b2371 100644 --- a/src/workbench/services/dashboard/type1SubView.ts +++ b/src/workbench/services/dashboard/type1SubView.ts @@ -1,6 +1,7 @@ import { IDashboardSubView, IDashboardSubViewOpts } from "src/workbench/services/dashboard/dashboardSubView"; +import { Disposable } from "src/base/common/dispose"; -export class Type1SubView implements IDashboardSubView { +export class Type1SubView extends Disposable implements IDashboardSubView { // [fields] @@ -10,7 +11,9 @@ export class Type1SubView implements IDashboardSubView { constructor( private opts: IDashboardSubViewOpts - ) {} + ) { + super(); + } // [public methods] @@ -37,8 +40,12 @@ export class Type1SubView implements IDashboardSubView { return subViewContainer; } - public dispose(): void { + public registerListeners(): void { + if (this.isDisposed()) { + return; + } + // this.__register(addDisposableListener(this._element, EventType.mousedown, e => this.__initDrag(e))); } // [private methods] diff --git a/src/workbench/services/dashboard/type2SubView.ts b/src/workbench/services/dashboard/type2SubView.ts index 7585ca6d7..c2f372376 100644 --- a/src/workbench/services/dashboard/type2SubView.ts +++ b/src/workbench/services/dashboard/type2SubView.ts @@ -1,6 +1,7 @@ import { IDashboardSubView, IDashboardSubViewOpts } from "src/workbench/services/dashboard/dashboardSubView"; +import { Disposable } from "src/base/common/dispose"; -export class Type2SubView implements IDashboardSubView { +export class Type2SubView extends Disposable implements IDashboardSubView { // [fields] @@ -10,7 +11,9 @@ export class Type2SubView implements IDashboardSubView { constructor( private opts: IDashboardSubViewOpts - ) {} + ) { + super(); + } // [public methods] @@ -47,8 +50,12 @@ export class Type2SubView implements IDashboardSubView { return subViewContainer; } - public dispose(): void { + public registerListeners(): void { + if (this.isDisposed()) { + return; + } + // this.__register(addDisposableListener(this._element, EventType.mousedown, e => this.__initDrag(e))); } // [private methods] From 10581162569fb15a83059ce8681a257052b54439 Mon Sep 17 00:00:00 2001 From: AAsteria <2329152202@qq.com> Date: Sun, 22 Dec 2024 22:44:23 +0800 Subject: [PATCH 5/6] [Fix] improve lifecycle management for `TypeXSubView` --- src/workbench/parts/workspace/workspace.ts | 2 +- .../services/dashboard/dashboardView.ts | 1 + .../services/dashboard/type1SubView.ts | 31 ++++++++++--------- .../services/dashboard/type2SubView.ts | 27 +++++++++++----- 4 files changed, 39 insertions(+), 22 deletions(-) diff --git a/src/workbench/parts/workspace/workspace.ts b/src/workbench/parts/workspace/workspace.ts index 2404cd354..0ef076087 100644 --- a/src/workbench/parts/workspace/workspace.ts +++ b/src/workbench/parts/workspace/workspace.ts @@ -65,7 +65,7 @@ export class WorkspaceComponent extends Component implements IWorkspaceService { subViews: [ { id: 'type1', title: 'Hello,' }, { id: 'type2', title: 'Pinned', content: ["Note 1", "Note 2"] }, - { id: 'type3', title: 'Recent', content: ["Item 1", "Item 2"] }, + { id: 'type2', title: 'Recent', content: ["Item 1", "Item 2"] }, ], }); diff --git a/src/workbench/services/dashboard/dashboardView.ts b/src/workbench/services/dashboard/dashboardView.ts index 4402d8bd6..324bde17b 100644 --- a/src/workbench/services/dashboard/dashboardView.ts +++ b/src/workbench/services/dashboard/dashboardView.ts @@ -28,6 +28,7 @@ export class DashboardView extends Component { .sort((a, b) => (a.priority || Priority.Low) - (b.priority || Priority.Low)) .forEach((subView) => { container.appendChild(subView.render(container)); + this.__register(subView); }); return container; diff --git a/src/workbench/services/dashboard/type1SubView.ts b/src/workbench/services/dashboard/type1SubView.ts index 9374b2371..d74ccfbbc 100644 --- a/src/workbench/services/dashboard/type1SubView.ts +++ b/src/workbench/services/dashboard/type1SubView.ts @@ -1,11 +1,13 @@ import { IDashboardSubView, IDashboardSubViewOpts } from "src/workbench/services/dashboard/dashboardSubView"; import { Disposable } from "src/base/common/dispose"; +import { addDisposableListener } from "src/base/browser/basic/dom"; export class Type1SubView extends Disposable implements IDashboardSubView { // [fields] public id: string = 'type1'; + private _subViewContainer: HTMLElement; // [constructor] @@ -13,14 +15,16 @@ export class Type1SubView extends Disposable implements IDashboardSubView { private opts: IDashboardSubViewOpts ) { super(); + this._subViewContainer = document.createElement("div"); + this._subViewContainer.classList.add("type1-subview"); + this._subViewContainer.setAttribute("data-id", this.opts.id); } // [public methods] public render(): HTMLElement { - const subViewContainer = document.createElement("div"); - subViewContainer.classList.add("type1-subview"); - subViewContainer.setAttribute("data-id", this.opts.id); + + this._subViewContainer.innerHTML = ""; // Create section header const header = document.createElement("div"); @@ -31,21 +35,20 @@ export class Type1SubView extends Disposable implements IDashboardSubView { title.textContent = this.opts.title || "Welcome to the Dashboard"; header.appendChild(title); - subViewContainer.appendChild(header); + this._subViewContainer.appendChild(header); // Add the New Note button const newNoteButton = this.__createNewNoteButton(); - subViewContainer.appendChild(newNoteButton); + this._subViewContainer.appendChild(newNoteButton); - return subViewContainer; + return this._subViewContainer; } - public registerListeners(): void { - if (this.isDisposed()) { - return; - } + // [protected methods] - // this.__register(addDisposableListener(this._element, EventType.mousedown, e => this.__initDrag(e))); + public override dispose(): void { + super.dispose(); + this._subViewContainer.remove(); } // [private methods] @@ -53,10 +56,10 @@ export class Type1SubView extends Disposable implements IDashboardSubView { private __createNewNoteButton(): HTMLElement { const button = document.createElement("button"); button.classList.add("new-note-btn"); - button.textContent = "New Note"; - button.addEventListener("click", () => { + button.textContent = "+ New Note"; + this.__register(addDisposableListener(button, "click", () => { console.log("New Note button clicked"); - }); + })); return button; } } diff --git a/src/workbench/services/dashboard/type2SubView.ts b/src/workbench/services/dashboard/type2SubView.ts index c2f372376..f77befb82 100644 --- a/src/workbench/services/dashboard/type2SubView.ts +++ b/src/workbench/services/dashboard/type2SubView.ts @@ -1,11 +1,13 @@ import { IDashboardSubView, IDashboardSubViewOpts } from "src/workbench/services/dashboard/dashboardSubView"; import { Disposable } from "src/base/common/dispose"; +import { addDisposableListener } from "src/base/browser/basic/dom"; export class Type2SubView extends Disposable implements IDashboardSubView { // [fields] public id: string = 'type2'; + private _subViewContainer: HTMLElement; // [constructor] @@ -13,14 +15,15 @@ export class Type2SubView extends Disposable implements IDashboardSubView { private opts: IDashboardSubViewOpts ) { super(); + this._subViewContainer = document.createElement("div"); + this._subViewContainer.classList.add("type2-subview"); + this._subViewContainer.setAttribute("data-id", this.opts.id); } // [public methods] public render(): HTMLElement { - const subViewContainer = document.createElement("div"); - subViewContainer.classList.add("type2-subview"); - subViewContainer.setAttribute("data-id", this.opts.id); + this._subViewContainer.innerHTML = ""; const header = document.createElement("div"); header.classList.add("section-header"); @@ -34,7 +37,7 @@ export class Type2SubView extends Disposable implements IDashboardSubView { const sortDropdown = this.__createSortDropdown(); header.appendChild(sortDropdown); - subViewContainer.appendChild(header); + this._subViewContainer.appendChild(header); // Create the content items const contentContainer = document.createElement("div"); @@ -46,16 +49,22 @@ export class Type2SubView extends Disposable implements IDashboardSubView { contentContainer.appendChild(itemElement); }); - subViewContainer.appendChild(contentContainer); - return subViewContainer; + this._subViewContainer.appendChild(contentContainer); + + return this._subViewContainer; } public registerListeners(): void { if (this.isDisposed()) { return; } + } - // this.__register(addDisposableListener(this._element, EventType.mousedown, e => this.__initDrag(e))); + // [protected methods] + + public override dispose(): void { + super.dispose(); + this._subViewContainer.remove(); } // [private methods] @@ -75,6 +84,10 @@ export class Type2SubView extends Disposable implements IDashboardSubView { dropdownText.textContent = "Last modified"; sortDropdown.appendChild(dropdownText); + this.__register(addDisposableListener(sortDropdown, "click", () => { + console.log("Sort dropdown clicked"); + })); + return sortDropdown; } } From 2fd73ac7f8c41a7711455eeed0d84623f5527afd Mon Sep 17 00:00:00 2001 From: AAsteria <2329152202@qq.com> Date: Sun, 22 Dec 2024 23:01:34 +0800 Subject: [PATCH 6/6] [Refactor] add slider and slider-items to dashboard --- .../services/dashboard/dashboardView.ts | 2 +- .../services/dashboard/media/dashboard.scss | 2 +- .../services/dashboard/type2SubView.ts | 32 ++++++++++++------- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/src/workbench/services/dashboard/dashboardView.ts b/src/workbench/services/dashboard/dashboardView.ts index 324bde17b..fb03f5ba0 100644 --- a/src/workbench/services/dashboard/dashboardView.ts +++ b/src/workbench/services/dashboard/dashboardView.ts @@ -48,7 +48,7 @@ export class DashboardView extends Component { case 'type1': return new Type1SubView(opts); case 'type2': - return new Type2SubView(opts); + return new Type2SubView(this.instantiationService, opts); default: panic(`Unsupported subview type: ${opts.id}`); } diff --git a/src/workbench/services/dashboard/media/dashboard.scss b/src/workbench/services/dashboard/media/dashboard.scss index 806634632..ce78bf5be 100644 --- a/src/workbench/services/dashboard/media/dashboard.scss +++ b/src/workbench/services/dashboard/media/dashboard.scss @@ -78,7 +78,7 @@ } } - .content-container { + .slider { display: flex; overflow-x: auto; gap: 1rem; diff --git a/src/workbench/services/dashboard/type2SubView.ts b/src/workbench/services/dashboard/type2SubView.ts index f77befb82..c00b468ab 100644 --- a/src/workbench/services/dashboard/type2SubView.ts +++ b/src/workbench/services/dashboard/type2SubView.ts @@ -1,6 +1,8 @@ import { IDashboardSubView, IDashboardSubViewOpts } from "src/workbench/services/dashboard/dashboardSubView"; import { Disposable } from "src/base/common/dispose"; import { addDisposableListener } from "src/base/browser/basic/dom"; +import { DashboardSlider } from "src/workbench/services/dashboard/dashboardSlider"; +import { IInstantiationService } from "src/platform/instantiation/common/instantiation"; export class Type2SubView extends Disposable implements IDashboardSubView { @@ -8,16 +10,23 @@ export class Type2SubView extends Disposable implements IDashboardSubView { public id: string = 'type2'; private _subViewContainer: HTMLElement; + private _slider: DashboardSlider; // [constructor] constructor( + @IInstantiationService instantiationService: IInstantiationService, private opts: IDashboardSubViewOpts ) { super(); + this.opts = opts; this._subViewContainer = document.createElement("div"); this._subViewContainer.classList.add("type2-subview"); this._subViewContainer.setAttribute("data-id", this.opts.id); + this._slider = new DashboardSlider( + instantiationService, + this.__createSliderItems(this.opts.content || []) + ); } // [public methods] @@ -39,17 +48,9 @@ export class Type2SubView extends Disposable implements IDashboardSubView { this._subViewContainer.appendChild(header); - // Create the content items - const contentContainer = document.createElement("div"); - contentContainer.classList.add("content-container"); - this.opts.content?.forEach(itemText => { - const itemElement = document.createElement("div"); - itemElement.textContent = itemText; - itemElement.classList.add("content-item"); - contentContainer.appendChild(itemElement); - }); - - this._subViewContainer.appendChild(contentContainer); + // Add slider to the subview container + const sliderElement = this._slider.createView(); + this._subViewContainer.appendChild(sliderElement); return this._subViewContainer; } @@ -90,4 +91,13 @@ export class Type2SubView extends Disposable implements IDashboardSubView { return sortDropdown; } + + private __createSliderItems(content: string[]): HTMLElement[] { + return content.map((itemText) => { + const itemElement = document.createElement("div"); + itemElement.textContent = itemText; + itemElement.classList.add("slider-item"); + return itemElement; + }); + } }