feat: add reordering to floating radial menu and fix weekly timetable step
Radial menu icon order (iPhone floating nav only): - Settings → 내비게이션 gains a '반원 메뉴 순서 편집' link (shown only when the radial style is selected) that opens a drag-to-reorder list of all 7 tabs. - Order is stored device-local via @AppStorage(SettingsKeys.radialTabOrder) (not CloudKit-synced); RadialMenu reads it through AppTab.radialOrder(from:), which always yields the full tab set and appends any missing/unknown value in default spec order — so partial or legacy strings can never drop a tab. - iPad/Mac sidebars and the watch target are untouched (they never read the key). History timetable date step: - The date header </> buttons now jump by a full week (7 days) when the timetable is in '일주일' view, and by 1 day otherwise (list / daily timetable). - The calendar popover and '오늘' still set the date directly, so those paths and record loading are unaffected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
This commit is contained in:
parent
8f2b5fab93
commit
a02e06a9df
@ -222,6 +222,22 @@ enum AppTab: String, CaseIterable, Identifiable {
|
||||
return tabs.isEmpty ? [.main] : Array(tabs.prefix(TabBarConfig.maxVisible))
|
||||
}
|
||||
|
||||
/// 저장된 반원 메뉴 순서를 항상 모든 탭을 포함한 배열로 변환.
|
||||
/// 방어 로직: 저장 문자열에 없거나(예: 나중에 추가된 탭) 잘못된 값은 기본(스펙) 순서로 맨 끝에 붙는다.
|
||||
static func radialOrder(from raw: String) -> [AppTab] {
|
||||
var result: [AppTab] = []
|
||||
var seen: Set<AppTab> = []
|
||||
for part in raw.split(separator: ",") {
|
||||
if let tab = AppTab(rawValue: String(part)), seen.insert(tab).inserted {
|
||||
result.append(tab)
|
||||
}
|
||||
}
|
||||
for tab in AppTab.allCases where !seen.contains(tab) {
|
||||
result.append(tab)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
var rootView: some View {
|
||||
switch self {
|
||||
|
||||
@ -293,8 +293,12 @@ struct HistoryView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func moveDay(_ delta: Int) {
|
||||
selectedDayKey = math.calendar.date(byAdding: .day, value: delta, to: selectedDayKey)!
|
||||
/// 날짜 이동 스텝은 보기 모드와 연동한다.
|
||||
/// 타임테이블 '일주일' 보기에서는 한 번에 한 주(7일)씩 점프해 즉시 이전/다음 주가 보인다.
|
||||
/// (목록·하루 타임테이블은 하루씩, 달력 팝업/‘오늘’로 날짜를 직접 선택하는 동작에는 영향 없음)
|
||||
private func moveDay(_ direction: Int) {
|
||||
let step = (mode == .timetable && timetableWeekly) ? 7 : 1
|
||||
selectedDayKey = math.calendar.date(byAdding: .day, value: direction * step, to: selectedDayKey)!
|
||||
}
|
||||
|
||||
// MARK: 데이터 수집 (구간 겹침으로 하루 경계 분할 자동 반영)
|
||||
|
||||
@ -65,6 +65,9 @@ struct RadialMenu: View {
|
||||
/// AppTab.rawValue (라우터의 tabSelection과 직접 연결)
|
||||
@Binding var selection: String
|
||||
|
||||
/// 사용자가 설정에서 지정한 반원 메뉴 아이콘 순서 (기기 로컬, 비동기화)
|
||||
@AppStorage(SettingsKeys.radialTabOrder) private var radialOrderRaw = ""
|
||||
|
||||
// 레이아웃 상수
|
||||
private let fabSize: CGFloat = 62
|
||||
private let itemSize: CGFloat = 52
|
||||
@ -74,7 +77,8 @@ struct RadialMenu: View {
|
||||
/// 아이콘 원 중심에서 탭 이름 라벨 중심까지의 거리(원 아래로)
|
||||
private let labelOffset: CGFloat = 40
|
||||
|
||||
private var tabs: [AppTab] { AppTab.allCases }
|
||||
/// 왼쪽 끝 → 위 → 오른쪽 끝 순으로 배치할 탭 (설정의 커스텀 순서를 따른다)
|
||||
private var tabs: [AppTab] { AppTab.radialOrder(from: radialOrderRaw) }
|
||||
|
||||
private var currentTab: AppTab {
|
||||
AppTab(rawValue: selection) ?? .main
|
||||
|
||||
@ -70,6 +70,14 @@ struct SettingsView: View {
|
||||
Text(style.label).tag(style.rawValue)
|
||||
}
|
||||
}
|
||||
// 반원 메뉴일 때만 아이콘 배치 순서를 편집할 수 있다
|
||||
if navStyleRaw == NavStyle.radial.rawValue {
|
||||
NavigationLink {
|
||||
RadialOrderView()
|
||||
} label: {
|
||||
Label("반원 메뉴 순서 편집", systemImage: "arrow.up.arrow.down")
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
Text("내비게이션")
|
||||
} footer: {
|
||||
@ -279,6 +287,42 @@ struct SettingsView: View {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - 반원(플로팅) 메뉴 아이콘 순서 편집 (iPhone 반원 모드 전용)
|
||||
|
||||
/// 반원 메뉴를 펼쳤을 때 왼쪽 끝 → 위 → 오른쪽 끝으로 배치될 탭 순서를 드래그로 바꾼다.
|
||||
/// 순서는 기기 로컬(비동기화)로만 저장되며, iPad·Mac 사이드바 순서에는 영향을 주지 않는다.
|
||||
struct RadialOrderView: View {
|
||||
@AppStorage(SettingsKeys.radialTabOrder) private var radialOrderRaw = ""
|
||||
@State private var order: [AppTab] = []
|
||||
|
||||
var body: some View {
|
||||
List {
|
||||
Section {
|
||||
ForEach(order) { tab in
|
||||
HStack(spacing: 12) {
|
||||
Image(systemName: tab.symbol)
|
||||
.foregroundStyle(AppTheme.green)
|
||||
.frame(width: 28)
|
||||
Text(tab.label)
|
||||
}
|
||||
}
|
||||
.onMove { source, destination in
|
||||
order.move(fromOffsets: source, toOffset: destination)
|
||||
radialOrderRaw = order.map(\.rawValue).joined(separator: ",")
|
||||
}
|
||||
} footer: {
|
||||
Text("반원 메뉴를 펼쳤을 때 왼쪽 끝에서 오른쪽 끝으로 이 순서대로 아이콘이 배치돼요. 손잡이를 끌어 순서를 바꾸세요.")
|
||||
}
|
||||
}
|
||||
.environment(\.editMode, .constant(.active))
|
||||
.scrollContentBackground(.hidden)
|
||||
.background(AppTheme.background)
|
||||
.navigationTitle("반원 메뉴 순서")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.onAppear { order = AppTab.radialOrder(from: radialOrderRaw) }
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - 프리미엄 안내
|
||||
|
||||
struct PremiumView: View {
|
||||
|
||||
@ -29,6 +29,8 @@ enum SettingsKeys {
|
||||
static let goalCardStyle = "settings.goalCardStyle"
|
||||
/// iPhone 내비게이션 방식. NavStyle rawValue ("tabBar" | "radial"). 기기 로컬(비동기화)
|
||||
static let navStyle = "settings.navStyle"
|
||||
/// 반원(플로팅) 메뉴에 펼칠 탭 순서. AppTab rawValue를 쉼표로 연결. 기기 로컬(비동기화)
|
||||
static let radialTabOrder = "settings.radialTabOrder"
|
||||
}
|
||||
|
||||
/// iPhone 전용 내비게이션 방식 (iPad·Mac 사이드바에는 적용되지 않음, 기기 로컬 설정)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user