From 0bcd150681bb82ab8bf69cd8731eb07d26af34a9 Mon Sep 17 00:00:00 2001 From: dlguszoo Date: Thu, 16 Jul 2026 05:03:14 +0900 Subject: [PATCH 01/10] =?UTF-8?q?[#38]=20fix:=20DVCategory,=20DVProjectCon?= =?UTF-8?q?tainer=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DVDesign/Sources/Components/DVCategory.swift | 12 ++++++------ .../Sources/Components/DVProjectContainer.swift | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Projects/DVDesign/Sources/Components/DVCategory.swift b/Projects/DVDesign/Sources/Components/DVCategory.swift index 3f104ae..6a4bbf9 100644 --- a/Projects/DVDesign/Sources/Components/DVCategory.swift +++ b/Projects/DVDesign/Sources/Components/DVCategory.swift @@ -64,21 +64,21 @@ extension DVCategory { private var iconView: some View { Image(systemName: systemImage) - .font(.system(size: 16, weight: .medium)) - .foregroundStyle(isSelected ? Color.dv(.white) : Color.dv(.gray700)) - .frame(width: 28, height: 28) + .dvFont(.headingLG) + .foregroundStyle(isSelected ? Color.dv(.white) : Color.dv(.gray800)) + .frame(width: 24, height: 24) } private var titleLabel: some View { Text(title) .dvFont(.bodyLG) - .foregroundStyle(isSelected ? Color.dv(.white) : Color.dv(.gray800)) + .foregroundStyle(isSelected ? Color.dv(.white) : Color.dv(.gray900)) } private var countLabel: some View { Text(count > 999 ? "999+" : "\(count)") .dvFont(.bodyMD) - .foregroundStyle(isSelected ? Color.dv(.white).opacity(0.8) : Color.dv(.gray600)) + .foregroundStyle(isSelected ? Color.dv(.white) : Color.dv(.gray600)) } } @@ -99,6 +99,6 @@ private struct DVCategoryButtonStyle: ButtonStyle { if isSelected { return Color.dv(.vaultGreen) } if isPressed { return Color.dv(.gray300) } if isHovered { return Color.dv(.gray200) } - return Color.dv(.gray100) + return Color.dv(.gray200) } } diff --git a/Projects/DVDesign/Sources/Components/DVProjectContainer.swift b/Projects/DVDesign/Sources/Components/DVProjectContainer.swift index 1c563e5..0c91707 100644 --- a/Projects/DVDesign/Sources/Components/DVProjectContainer.swift +++ b/Projects/DVDesign/Sources/Components/DVProjectContainer.swift @@ -27,7 +27,7 @@ public struct DVProjectContainer: View { Spacer(minLength: 8) countLabel } - .padding(6) + .padding(2) .frame(minWidth: 120, alignment: .leading) } } @@ -39,13 +39,13 @@ extension DVProjectContainer { private var projectIcon: some View { Image(systemName: "tray") .dvFont(.captionLG) - .foregroundStyle(.primary) + .foregroundStyle(Color.dv(.gray900)) } private var nameLabel: some View { Text(name) .dvFont(.bodyMD) - .foregroundStyle(.primary) + .foregroundStyle(Color.dv(.gray900)) .lineLimit(1) .truncationMode(.tail) .frame(minWidth: 40, alignment: .leading) @@ -54,7 +54,7 @@ extension DVProjectContainer { private var countLabel: some View { Text(count > 999 ? "999+" : "\(count)") .dvFont(.bodyMD) - .foregroundStyle(.secondary) + .foregroundStyle(Color.dv(.gray400)) .fixedSize() } } From d60da2c6b1cc0f9c860d72ea3fc4e36e47ad2552 Mon Sep 17 00:00:00 2001 From: dlguszoo Date: Thu, 16 Jul 2026 05:18:50 +0900 Subject: [PATCH 02/10] =?UTF-8?q?[#38]=20feat:=20SidebarFeature=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SidebarFilter, SidebarSelection enum 정의 및 SidebarFeature 구현. SidebarSelection으로 filter/project 선택을 단일 상태로 표현해 상호 배타성 보장. --- .../Features/Sidebar/SidebarFeature.swift | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 Projects/DVPresentation/Sources/Features/Sidebar/SidebarFeature.swift diff --git a/Projects/DVPresentation/Sources/Features/Sidebar/SidebarFeature.swift b/Projects/DVPresentation/Sources/Features/Sidebar/SidebarFeature.swift new file mode 100644 index 0000000..473a94b --- /dev/null +++ b/Projects/DVPresentation/Sources/Features/Sidebar/SidebarFeature.swift @@ -0,0 +1,115 @@ +// Copyright © 2026 Devault. All rights reserved + +import Foundation + +import ComposableArchitecture + +// MARK: - SidebarFilter + +public enum SidebarFilter: Equatable, CaseIterable, Hashable { + case all + case starred + case expired + case deleted + + var title: String { + switch self { + case .all: "All" + case .starred: "Star" + case .expired: "Expired" + case .deleted: "Deleted" + } + } + + var icon: String { + switch self { + case .all: "square.grid.2x2.fill" + case .starred: "star.fill" + case .expired: "exclamationmark" + case .deleted: "trash.fill" + } + } +} + +// MARK: - SidebarSelection + +public enum SidebarSelection: Equatable { + case filter(SidebarFilter) + case project(Int) +} + +// MARK: - SidebarFeature + +@Reducer +public struct SidebarFeature { + + // MARK: - State + + @ObservableState + public struct State: Equatable { + var selection: SidebarSelection = .filter(.all) + var isProjectSectionExpanded: Bool = true + + public init() {} + } + + // MARK: - Action + + public enum Action: Equatable { + + // MARK: - View + + case task + case didSelect(SidebarSelection) + case didTapAddButton + case didTapSettingsButton + case didTapAddProject + case didToggleProjectSection + + // MARK: - Delegate + + case delegate(Delegate) + + public enum Delegate: Equatable { + case selectionChanged(SidebarSelection) + case addButtonTapped + case settingsButtonTapped + case addProjectTapped + } + } + + // MARK: - Init + + public init() {} + + // MARK: - Body + + public var body: some ReducerOf { + Reduce { state, action in + switch action { + case .task: + return .none + + case .didSelect(let selection): + state.selection = selection + return .send(.delegate(.selectionChanged(selection))) + + case .didTapAddButton: + return .send(.delegate(.addButtonTapped)) + + case .didTapSettingsButton: + return .send(.delegate(.settingsButtonTapped)) + + case .didTapAddProject: + return .send(.delegate(.addProjectTapped)) + + case .didToggleProjectSection: + state.isProjectSectionExpanded.toggle() + return .none + + case .delegate: + return .none + } + } + } +} From dfcb8d00f953bd1b40358d91d424165627c78a38 Mon Sep 17 00:00:00 2001 From: dlguszoo Date: Thu, 16 Jul 2026 05:19:08 +0900 Subject: [PATCH 03/10] =?UTF-8?q?[#38]=20feat:=20MainFeature=EC=97=90=20Si?= =?UTF-8?q?debarFeature=20=EC=9E=90=EC=8B=9D=20reducer=20=EC=97=B0?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Scope으로 SidebarFeature를 MainFeature에 연결하고 SidebarView를 MainView sidebar 컬럼에 주입. --- .../Sources/Features/Main/MainFeature.swift | 38 +++++++++++++++++++ .../Sources/Features/Main/MainView.swift | 3 +- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/Projects/DVPresentation/Sources/Features/Main/MainFeature.swift b/Projects/DVPresentation/Sources/Features/Main/MainFeature.swift index 1dfa69f..446f49c 100644 --- a/Projects/DVPresentation/Sources/Features/Main/MainFeature.swift +++ b/Projects/DVPresentation/Sources/Features/Main/MainFeature.swift @@ -14,6 +14,7 @@ public struct MainFeature { @ObservableState public struct State: Equatable { public var columnVisibility: NavigationSplitViewVisibility = .all + var sidebar: SidebarFeature.State = .init() public init() {} } @@ -27,6 +28,10 @@ public struct MainFeature { case binding(BindingAction) case task + // MARK: - Child + + case sidebar(SidebarFeature.Action) + // MARK: - Delegate case delegate(Delegate) @@ -42,6 +47,9 @@ public struct MainFeature { public var body: some ReducerOf { BindingReducer() + Scope(state: \.sidebar, action: \.sidebar) { + SidebarFeature() + } Reduce { state, action in switch action { case .binding: @@ -50,9 +58,39 @@ public struct MainFeature { case .task: return .none + case .sidebar(.delegate(let delegate)): + return handleSidebarDelegate(&state, delegate: delegate) + + case .sidebar: + return .none + case .delegate: return .none } } } } + +// MARK: - Private + +extension MainFeature { + + private func handleSidebarDelegate( + _ state: inout State, + delegate: SidebarFeature.Action.Delegate + ) -> Effect { + switch delegate { + case .selectionChanged: + return .none + + case .addButtonTapped: + return .none + + case .settingsButtonTapped: + return .none + + case .addProjectTapped: + return .none + } + } +} diff --git a/Projects/DVPresentation/Sources/Features/Main/MainView.swift b/Projects/DVPresentation/Sources/Features/Main/MainView.swift index dbfe0ec..9ae964a 100644 --- a/Projects/DVPresentation/Sources/Features/Main/MainView.swift +++ b/Projects/DVPresentation/Sources/Features/Main/MainView.swift @@ -37,7 +37,8 @@ extension MainView { } private var sidebar: some View { - Text("Sidebar") + SidebarView(store: store.scope(state: \.sidebar, action: \.sidebar)) + .navigationSplitViewColumnWidth(min: 200, ideal: 240, max: 300) } private var contentColumn: some View { From 713316ec93c94e85bcca336d6447347e6f829058 Mon Sep 17 00:00:00 2001 From: dlguszoo Date: Thu, 16 Jul 2026 05:22:20 +0900 Subject: [PATCH 04/10] =?UTF-8?q?[#38]=20feat:=20SidebarView=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DVCategory, DVProjectContainer 컴포넌트를 활용한 사이드바 UI 구현. filterGrid, projectSection, bottomBar로 구성. --- .../Features/Sidebar/SidebarView.swift | 171 ++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 Projects/DVPresentation/Sources/Features/Sidebar/SidebarView.swift diff --git a/Projects/DVPresentation/Sources/Features/Sidebar/SidebarView.swift b/Projects/DVPresentation/Sources/Features/Sidebar/SidebarView.swift new file mode 100644 index 0000000..2bb483b --- /dev/null +++ b/Projects/DVPresentation/Sources/Features/Sidebar/SidebarView.swift @@ -0,0 +1,171 @@ +// Copyright © 2026 Devault. All rights reserved + +import SwiftUI + +import ComposableArchitecture +import DVDesign + +// MARK: - SidebarView + +struct SidebarView: View { + + // MARK: - Properties + + @Bindable var store: StoreOf + + // MARK: - Body + + var body: some View { + content + .task { store.send(.task) } + } +} + +// MARK: - Subviews + +extension SidebarView { + + private var content: some View { + VStack(spacing: 10) { + logoView + .padding(.leading, 4) + filterGrid + .padding(.bottom, 10) + projectSection + Spacer() + bottomBar + .padding(.bottom, 10) + } + .padding(.horizontal, 12) + } + + // 임시 로고뷰 입니다. + private var logoView: some View { + HStack { + Rectangle() + .fill(Color.red) + .frame(width: 52, height: 28) + .overlay( + Text("LOGO") + .font(.system(size: 18, weight: .semibold)) + .foregroundStyle(.black) + ) + Spacer() + } + } + + private var filterGrid: some View { + LazyVGrid( + columns: [GridItem(.flexible()), GridItem(.flexible())], + spacing: 12 + ) { + ForEach(SidebarFilter.allCases, id: \.self) { filter in + DVCategory( + title: filter.title, + count: 0, + systemImage: filter.icon, + isSelected: store.selection == .filter(filter) + ) { + store.send(.didSelect(.filter(filter))) + } + } + } + } + + private var projectSection: some View { + VStack(spacing: 12) { + projectSectionHeader + .padding(.horizontal, 8) + if store.isProjectSectionExpanded { + projectList + } + } + } + + private var projectSectionHeader: some View { + HStack(spacing: 11) { + Text("Project") + .dvFont(.captionMDSemibold) + .foregroundStyle(Color.dv(.vaultGreen)) + Spacer() + projectHeaderButton(icon: "plus.circle") { store.send(.didTapAddProject) } + projectHeaderButton( + icon: store.isProjectSectionExpanded ? "chevron.down" : "chevron.up" + ) { + store.send(.didToggleProjectSection) + } + } + } + + // TODO: - contextMenu 확정 후 추후에 Action 추가 + private var projectList: some View { + let projects = ["Project A", "Project B", "Project C"] + // List(selection:)은 Int?를 요구하지만 store.selection은 SidebarSelection(enum)이라 + // 타입 불일치로 $store 직접 바인딩 불가 → 의도적 manual Binding 사용 + return List( + selection: Binding( + get: { + if case .project(let index) = store.selection { return index } + return nil + }, + set: { index in + if let index { store.send(.didSelect(.project(index))) } + } + ) + ) { + ForEach(projects.indices, id: \.self) { index in + DVProjectContainer(name: projects[index], count: 0) + .tag(index) + .contextMenu { + Button("Rename") {} + Button("New Secret") {} + Divider() + Button("Delete Project", role: .destructive) {} + } + } + } + .listStyle(.sidebar) + .scrollContentBackground(.hidden) + .frame(maxHeight: 200) + .padding(.horizontal, -12) + } + + private var bottomBar: some View { + HStack { + circleIconButton(icon: "gearshape") { store.send(.didTapSettingsButton) } + Spacer() + circleIconButton(icon: "plus") { store.send(.didTapAddButton) } + } + } + + private func projectHeaderButton(icon: String, action: @escaping () -> Void) -> some View { + Button(action: action) { + Image(systemName: icon) + .dvFont(.captionLG) + .foregroundStyle(Color.dv(.gray500)) + } + .buttonStyle(.plain) + } + + private func circleIconButton(icon: String, action: @escaping () -> Void) -> some View { + Button(action: action) { + Image(systemName: icon) + .dvFont(.captionLG) + .frame(width: 32, height: 32) + .background(Color.dv(.vaultGreen), in: Circle()) + .foregroundStyle(Color.dv(.white)) + } + .buttonStyle(.plain) + } +} + +// MARK: - Preview + +#Preview { + SidebarView( + store: Store(initialState: SidebarFeature.State()) { + SidebarFeature() + } + ) + .frame(width: 240) +} From 9b5f61e73d3ee0f42c824321141b217cdacef72d Mon Sep 17 00:00:00 2001 From: dlguszoo Date: Thu, 16 Jul 2026 05:30:31 +0900 Subject: [PATCH 05/10] =?UTF-8?q?[#38]=20fix:=20trash=20=EC=95=84=EC=9D=B4?= =?UTF-8?q?=EC=BD=98=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Features/Sidebar/SidebarFeature.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Projects/DVPresentation/Sources/Features/Sidebar/SidebarFeature.swift b/Projects/DVPresentation/Sources/Features/Sidebar/SidebarFeature.swift index 473a94b..0300ae4 100644 --- a/Projects/DVPresentation/Sources/Features/Sidebar/SidebarFeature.swift +++ b/Projects/DVPresentation/Sources/Features/Sidebar/SidebarFeature.swift @@ -26,7 +26,7 @@ public enum SidebarFilter: Equatable, CaseIterable, Hashable { case .all: "square.grid.2x2.fill" case .starred: "star.fill" case .expired: "exclamationmark" - case .deleted: "trash.fill" + case .deleted: "trash" } } } From 801a973fd063e1f63ce14b9b6265ace3a9a8abd9 Mon Sep 17 00:00:00 2001 From: dlguszoo Date: Sun, 19 Jul 2026 18:37:18 +0900 Subject: [PATCH 06/10] =?UTF-8?q?[#38]=20fix:=20gray300=20=EC=BB=AC?= =?UTF-8?q?=EB=9F=AC=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95=20=EB=B0=8F?= =?UTF-8?q?=20DVCategory=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../color/gray300.colorset/Contents.json | 12 ++++++------ .../DVDesign/Sources/Components/DVCategory.swift | 7 +++---- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/Projects/DVDesign/Resources/Assets.xcassets/color/gray300.colorset/Contents.json b/Projects/DVDesign/Resources/Assets.xcassets/color/gray300.colorset/Contents.json index dbd79ae..c31e976 100644 --- a/Projects/DVDesign/Resources/Assets.xcassets/color/gray300.colorset/Contents.json +++ b/Projects/DVDesign/Resources/Assets.xcassets/color/gray300.colorset/Contents.json @@ -5,9 +5,9 @@ "color-space" : "srgb", "components" : { "alpha" : "1.000", - "blue" : "0xEE", - "green" : "0xF0", - "red" : "0xEB" + "blue" : "0xE4", + "green" : "0xE6", + "red" : "0xE2" } }, "idiom" : "universal" @@ -23,9 +23,9 @@ "color-space" : "srgb", "components" : { "alpha" : "1.000", - "blue" : "0xEE", - "green" : "0xF0", - "red" : "0xEB" + "blue" : "0xE4", + "green" : "0xE6", + "red" : "0xE2" } }, "idiom" : "universal" diff --git a/Projects/DVDesign/Sources/Components/DVCategory.swift b/Projects/DVDesign/Sources/Components/DVCategory.swift index 6a4bbf9..2dddeca 100644 --- a/Projects/DVDesign/Sources/Components/DVCategory.swift +++ b/Projects/DVDesign/Sources/Components/DVCategory.swift @@ -91,14 +91,13 @@ private struct DVCategoryButtonStyle: ButtonStyle { func makeBody(configuration: Configuration) -> some View { configuration.label - .background(backgroundColor(isPressed: configuration.isPressed)) + .background(backgroundColor) .clipShape(RoundedRectangle(cornerRadius: 16)) } - private func backgroundColor(isPressed: Bool) -> Color { + private var backgroundColor: Color { if isSelected { return Color.dv(.vaultGreen) } - if isPressed { return Color.dv(.gray300) } - if isHovered { return Color.dv(.gray200) } + if isHovered { return Color.dv(.gray300) } return Color.dv(.gray200) } } From c9585c1bad62a959f016929e67979bae00203611 Mon Sep 17 00:00:00 2001 From: dlguszoo Date: Sun, 19 Jul 2026 18:37:35 +0900 Subject: [PATCH 07/10] =?UTF-8?q?[#38]=20fix:=20navigationSplitViewColumnW?= =?UTF-8?q?idth=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Projects/DVPresentation/Sources/Features/Main/MainView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Projects/DVPresentation/Sources/Features/Main/MainView.swift b/Projects/DVPresentation/Sources/Features/Main/MainView.swift index 9ae964a..7bcf8f3 100644 --- a/Projects/DVPresentation/Sources/Features/Main/MainView.swift +++ b/Projects/DVPresentation/Sources/Features/Main/MainView.swift @@ -38,7 +38,7 @@ extension MainView { private var sidebar: some View { SidebarView(store: store.scope(state: \.sidebar, action: \.sidebar)) - .navigationSplitViewColumnWidth(min: 200, ideal: 240, max: 300) + .navigationSplitViewColumnWidth(min: 200, ideal: 250, max: 270) } private var contentColumn: some View { From 9ed5491f7f99e1d72880c86f1d132f280219bf51 Mon Sep 17 00:00:00 2001 From: dlguszoo Date: Sun, 19 Jul 2026 18:38:52 +0900 Subject: [PATCH 08/10] =?UTF-8?q?[#38]=20feat:=20SidebarFeature=20State=20?= =?UTF-8?q?=ED=94=84=EB=A1=9C=ED=8D=BC=ED=8B=B0=20=EC=A0=91=EA=B7=BC=20?= =?UTF-8?q?=EC=A0=9C=EC=96=B4=EC=9E=90=20public=20internal(set)=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 외부 모듈에서 State를 관찰해야 하는 시점을 대비해 selection, isProjectSectionExpanded를 public internal(set)으로 변경. getter는 외부 공개, setter는 모듈 내부(reducer)만 접근 가능. --- .../Sources/Features/Sidebar/SidebarFeature.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Projects/DVPresentation/Sources/Features/Sidebar/SidebarFeature.swift b/Projects/DVPresentation/Sources/Features/Sidebar/SidebarFeature.swift index 0300ae4..178e91c 100644 --- a/Projects/DVPresentation/Sources/Features/Sidebar/SidebarFeature.swift +++ b/Projects/DVPresentation/Sources/Features/Sidebar/SidebarFeature.swift @@ -47,8 +47,8 @@ public struct SidebarFeature { @ObservableState public struct State: Equatable { - var selection: SidebarSelection = .filter(.all) - var isProjectSectionExpanded: Bool = true + public internal(set) var selection: SidebarSelection = .filter(.all) + public internal(set) var isProjectSectionExpanded: Bool = true public init() {} } From 82d13e7b2dc0aca2d8cdb227fdc90f1e3b002bf0 Mon Sep 17 00:00:00 2001 From: dlguszoo Date: Sun, 19 Jul 2026 18:39:21 +0900 Subject: [PATCH 09/10] =?UTF-8?q?[#38]=20fix:=20SidebarView=20PR=20?= =?UTF-8?q?=EB=A6=AC=EB=B7=B0=20=EB=B0=98=EC=98=81=20=EB=B0=8F=20=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EC=95=84=EC=9B=83=20=EB=B2=84=EA=B7=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - List maxHeight 제한 제거 - 접근성 레이블 추가: Add Project, Collapse/Expand Projects, Settings, Add Secret - 프리뷰를 Group으로 통합, 너비 200/250/270pt 대응 - collapsed 시 Spacer(minLength: 0) 추가로 bottomBar 하단 고정 버그 수정 - List deselect nil 처리 TODO 주석 추가 --- .../Features/Sidebar/SidebarView.swift | 38 +++++++++++++++---- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/Projects/DVPresentation/Sources/Features/Sidebar/SidebarView.swift b/Projects/DVPresentation/Sources/Features/Sidebar/SidebarView.swift index 2bb483b..37cb2a9 100644 --- a/Projects/DVPresentation/Sources/Features/Sidebar/SidebarView.swift +++ b/Projects/DVPresentation/Sources/Features/Sidebar/SidebarView.swift @@ -32,7 +32,7 @@ extension SidebarView { filterGrid .padding(.bottom, 10) projectSection - Spacer() + .frame(maxHeight: .infinity) bottomBar .padding(.bottom, 10) } @@ -78,6 +78,8 @@ extension SidebarView { .padding(.horizontal, 8) if store.isProjectSectionExpanded { projectList + } else { + Spacer(minLength: 0) } } } @@ -89,11 +91,13 @@ extension SidebarView { .foregroundStyle(Color.dv(.vaultGreen)) Spacer() projectHeaderButton(icon: "plus.circle") { store.send(.didTapAddProject) } + .accessibilityLabel("Add Project") projectHeaderButton( icon: store.isProjectSectionExpanded ? "chevron.down" : "chevron.up" ) { store.send(.didToggleProjectSection) } + .accessibilityLabel(store.isProjectSectionExpanded ? "Collapse Projects" : "Expand Projects") } } @@ -110,6 +114,7 @@ extension SidebarView { }, set: { index in if let index { store.send(.didSelect(.project(index))) } + // TODO: nil(deselect) 처리 — content 컬럼 연결 시 결정 } ) ) { @@ -126,15 +131,16 @@ extension SidebarView { } .listStyle(.sidebar) .scrollContentBackground(.hidden) - .frame(maxHeight: 200) .padding(.horizontal, -12) } private var bottomBar: some View { HStack { circleIconButton(icon: "gearshape") { store.send(.didTapSettingsButton) } + .accessibilityLabel("Settings") Spacer() circleIconButton(icon: "plus") { store.send(.didTapAddButton) } + .accessibilityLabel("Add Secret") } } @@ -162,10 +168,26 @@ extension SidebarView { // MARK: - Preview #Preview { - SidebarView( - store: Store(initialState: SidebarFeature.State()) { - SidebarFeature() - } - ) - .frame(width: 240) + Group { + SidebarView( + store: Store(initialState: SidebarFeature.State()) { + SidebarFeature() + } + ) + .frame(width: 200) + + SidebarView( + store: Store(initialState: SidebarFeature.State()) { + SidebarFeature() + } + ) + .frame(width: 250) + + SidebarView( + store: Store(initialState: SidebarFeature.State()) { + SidebarFeature() + } + ) + .frame(width: 270) + } } From 58ff27fa46812e2cd15f6065aca8b52973f8737f Mon Sep 17 00:00:00 2001 From: dlguszoo Date: Sun, 19 Jul 2026 18:45:57 +0900 Subject: [PATCH 10/10] =?UTF-8?q?[#38]=20fix:=20DVCategory=20isPressed=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Projects/DVDesign/Sources/Components/DVCategory.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Projects/DVDesign/Sources/Components/DVCategory.swift b/Projects/DVDesign/Sources/Components/DVCategory.swift index 2dddeca..d3223ca 100644 --- a/Projects/DVDesign/Sources/Components/DVCategory.swift +++ b/Projects/DVDesign/Sources/Components/DVCategory.swift @@ -91,12 +91,13 @@ private struct DVCategoryButtonStyle: ButtonStyle { func makeBody(configuration: Configuration) -> some View { configuration.label - .background(backgroundColor) + .background(backgroundColor(isPressed: configuration.isPressed)) .clipShape(RoundedRectangle(cornerRadius: 16)) } - private var backgroundColor: Color { + private func backgroundColor(isPressed: Bool) -> Color { if isSelected { return Color.dv(.vaultGreen) } + if isPressed { return Color.dv(.gray400) } if isHovered { return Color.dv(.gray300) } return Color.dv(.gray200) }