Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ extension NativeCalendarRootModel {
let items = filteredPaletteSections().flatMap(\.items)
guard !items.isEmpty else { return }
let index = items.firstIndex { $0.id == commandPaletteStore.selectedCommandId } ?? 0
let next = (index + delta % items.count + items.count) % items.count
let count = items.count
let next = ((index + delta) % count + count) % count
commandPaletteStore.select(commandId: items[next].id)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,13 @@ extension NativeCalendarRootModel {
return
}

guard let prefix = DayJumpPrefixes.dayPrefix(forShiftedLetter: letter), prefix != "s" else { return }
guard let prefix = DayJumpPrefixes.dayPrefix(forShiftedLetter: letter) else { return }
selectJumpDay(prefix: prefix)
}

private func selectJumpDay(prefix: String) {
let calendar = EffectiveTimeZone.calendar
for dayKey in visibleDateKeysForJump() {
for dayKey in visibleDateKeys() {
guard let date = CompassDateParsing.calendarDateInEffectiveTimeZone(dayKey) else { continue }
let weekday = calendar.component(.weekday, from: date) - 1
guard DayJumpPrefixes.prefix(forWeekday: weekday) == prefix else { continue }
Expand Down Expand Up @@ -83,13 +83,4 @@ extension NativeCalendarRootModel {
}
}

private func visibleDateKeysForJump() -> [String] {
let calendar = EffectiveTimeZone.calendar
return (0 ..< viewStore.visibleDayCount).compactMap { offset in
guard let day = calendar.date(byAdding: .day, value: offset, to: startOfView) else {
return nil
}
return CompassDateParsing.formatCalendarDay(day)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -948,7 +948,7 @@ public final class NativeCalendarRootModel {
) ?? startOfView
}

private func visibleDateKeys() -> [String] {
func visibleDateKeys() -> [String] {
let calendar = EffectiveTimeZone.calendar
return (0 ..< viewStore.visibleDayCount).compactMap { offset in
guard let day = calendar.date(byAdding: .day, value: offset, to: startOfView) else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,22 +29,12 @@ public enum UITestLaunchPolicy {

/// When set, focuses this grid event once the demo fixture layout is ready (no pointer hint).
public static var initialGridFocusEventId: String? {
let args = ProcessInfo.processInfo.arguments
guard let index = args.firstIndex(of: "-COMPASS_UI_TEST_INITIAL_GRID_FOCUS_EVENT"),
index + 1 < args.count
else { return nil }
let value = args[index + 1]
return value.isEmpty ? nil : value
stringValue(forFlag: "-COMPASS_UI_TEST_INITIAL_GRID_FOCUS_EVENT")
}

/// When set, a grid tap that would show the scroll hint focuses this event id instead.
public static var gridClickFocusEventId: String? {
let args = ProcessInfo.processInfo.arguments
guard let index = args.firstIndex(of: "-COMPASS_UI_TEST_GRID_CLICK_FOCUS_EVENT"),
index + 1 < args.count
else { return nil }
let value = args[index + 1]
return value.isEmpty ? nil : value
stringValue(forFlag: "-COMPASS_UI_TEST_GRID_CLICK_FOCUS_EVENT")
}

/// Opens the native edit form for the initially focused grid event once layout is ready.
Expand Down Expand Up @@ -98,11 +88,17 @@ public enum UITestLaunchPolicy {

/// Seeds the open event form title (XCUITest typing is flaky for recurring edits in CI).
public static var presetEventFormTitle: String? {
let args = ProcessInfo.processInfo.arguments
guard let index = args.firstIndex(of: "-COMPASS_UI_TEST_EVENT_FORM_TITLE"),
index + 1 < args.count
else { return nil }
let value = args[index + 1]
stringValue(forFlag: "-COMPASS_UI_TEST_EVENT_FORM_TITLE")
}

private static func stringValue(
forFlag flag: String,
in arguments: [String] = ProcessInfo.processInfo.arguments
) -> String? {
guard let index = arguments.firstIndex(of: flag), index + 1 < arguments.count else {
return nil
}
let value = arguments[index + 1]
return value.isEmpty ? nil : value
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Foundation
public enum CommandPaletteSearchLogic {
private static let minSubsequenceTokenLength = 3

public struct ScoredItem: Hashable, Sendable {
private struct ScoredItem: Hashable, Sendable {
public let item: CommandPaletteSearchItem
public let score: Int
}
Expand Down Expand Up @@ -130,7 +130,7 @@ public enum CommandPaletteSearchLogic {

private static func hasWordStartingWith(haystack: String, token: String) -> Bool {
haystack.split(whereSeparator: { $0.isWhitespace || $0 == "-" })
.contains { $0.lowercased().hasPrefix(token) }
.contains { $0.hasPrefix(token) }
}

private static func isSubsequence(haystack: String, token: String) -> Bool {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import SwiftUI
/// Web `FormCard`: grouped fields on `surface-overlay` with default radius.
struct EventFormCard<Content: View>: View {
@Environment(\.nativeWebTheme) private var theme
var highlighted = false
@ViewBuilder var content: () -> Content

var body: some View {
Expand All @@ -12,5 +13,11 @@ struct EventFormCard<Content: View>: View {
.frame(maxWidth: .infinity, alignment: .leading)
.background(theme.surfaceOverlayColor)
.clipShape(RoundedRectangle(cornerRadius: NativeWebLayoutTokens.radiusDefault))
.overlay {
if highlighted {
RoundedRectangle(cornerRadius: NativeWebLayoutTokens.radiusDefault)
.stroke(theme.accentColor, lineWidth: 1)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,18 @@ struct EventFormCompactDateField: View {
private func commitTypedDate() {
let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines)
if trimmed.isEmpty {
text = EventFormDateFormat.formatLabel(for: date)
revertTextToBoundDate()
return
}
if let parsed = EventFormDateFormat.parseTypedLabel(trimmed, now: date) {
date = parsed
text = EventFormDateFormat.formatLabel(for: parsed)
return
}
revertTextToBoundDate()
}

private func revertTextToBoundDate() {
text = EventFormDateFormat.formatLabel(for: date)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ struct EventFormConferenceSection: View {
@Environment(\.nativeWebTheme) private var theme
@Bindable var model: NativeCalendarRootModel
let draft: GridEventDraft
let focusField: EventFormField

var body: some View {
if model.showCreateConferenceToggle(for: draft),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ struct EventFormScheduleSection: View {
@Environment(\.nativeWebTheme) private var theme
@Bindable var model: NativeCalendarRootModel
let draft: GridEventDraft
let focusField: EventFormField

private var scheduleLocked: Bool {
model.isScheduleLocked(for: draft)
Expand All @@ -23,57 +22,22 @@ struct EventFormScheduleSection: View {
.accessibilityIdentifier("compass-event-form-allday")

if draft.schedule.kind == .timed {
HStack(spacing: 8) {
EventFormCompactDateField(
date: Binding(
get: { draft.schedule.start },
set: { updateStart(mergeCalendarDay(into: draft.schedule.start, day: $0)) }
),
accessibilityIdentifier: "compass-event-form-start-date"
)
.disabled(scheduleLocked)
.pageJumpChipAnchor(id: "form-start")

DatePicker(
"Start time",
selection: Binding(
get: { draft.schedule.start },
set: { updateStart($0) }
),
displayedComponents: [.hourAndMinute]
)
.labelsHidden()
.controlSize(.small)
.frame(minHeight: 38)
.disabled(scheduleLocked)
.accessibilityIdentifier("compass-event-form-start")
}

HStack(spacing: 8) {
EventFormCompactDateField(
date: Binding(
get: { draft.schedule.end },
set: { updateEnd(mergeCalendarDay(into: draft.schedule.end, day: $0)) }
),
accessibilityIdentifier: "compass-event-form-end-date"
)
.disabled(scheduleLocked)
.pageJumpChipAnchor(id: "form-end")

DatePicker(
"End time",
selection: Binding(
get: { draft.schedule.end },
set: { updateEnd($0) }
),
displayedComponents: [.hourAndMinute]
)
.labelsHidden()
.controlSize(.small)
.frame(minHeight: 38)
.disabled(scheduleLocked)
.accessibilityIdentifier("compass-event-form-end")
}
timedRow(
date: draft.schedule.start,
dateAccessibilityIdentifier: "compass-event-form-start-date",
timeLabel: "Start time",
timeAccessibilityIdentifier: "compass-event-form-start",
pageJumpId: "form-start",
update: updateStart
)
timedRow(
date: draft.schedule.end,
dateAccessibilityIdentifier: "compass-event-form-end-date",
timeLabel: "End time",
timeAccessibilityIdentifier: "compass-event-form-end",
pageJumpId: "form-end",
update: updateEnd
)
} else {
EventFormCompactDateField(
date: Binding(
Expand Down Expand Up @@ -111,6 +75,42 @@ struct EventFormScheduleSection: View {
}
}

@ViewBuilder
private func timedRow(
date: Date,
dateAccessibilityIdentifier: String,
timeLabel: String,
timeAccessibilityIdentifier: String,
pageJumpId: String,
update: @escaping (Date) -> Void
) -> some View {
HStack(spacing: 8) {
EventFormCompactDateField(
date: Binding(
get: { date },
set: { update(mergeCalendarDay(into: date, day: $0)) }
),
accessibilityIdentifier: dateAccessibilityIdentifier
)
.disabled(scheduleLocked)
.pageJumpChipAnchor(id: pageJumpId)

DatePicker(
timeLabel,
selection: Binding(
get: { date },
set: { update($0) }
),
displayedComponents: [.hourAndMinute]
)
.labelsHidden()
.controlSize(.small)
.frame(minHeight: 38)
.disabled(scheduleLocked)
.accessibilityIdentifier(timeAccessibilityIdentifier)
}
}

private func mergeCalendarDay(into scheduleDate: Date, day: Date) -> Date {
var calendar = EffectiveTimeZone.calendar
let time = calendar.dateComponents([.hour, .minute, .second], from: scheduleDate)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,7 @@ public struct EventFormView: View {
VStack(alignment: .leading, spacing: 10) {
EventFormScheduleSection(
model: model,
draft: draft,
focusField: model.eventFormFocusedField
draft: draft
)
EventFormRecurrenceSection(
model: model,
Expand Down Expand Up @@ -82,7 +81,7 @@ public struct EventFormView: View {
}
}
.id(EventFormField.calendar)
EventFormCard {
EventFormCard(highlighted: isLogisticsFieldFocused(model.eventFormFocusedField)) {
VStack(alignment: .leading, spacing: 10) {
EventFormLocationField(
model: model,
Expand All @@ -92,8 +91,7 @@ public struct EventFormView: View {
.disabled(isReadOnly)
EventFormConferenceSection(
model: model,
draft: draft,
focusField: model.eventFormFocusedField
draft: draft
)
EventFormAttendeesSection(
model: model,
Expand All @@ -103,14 +101,8 @@ public struct EventFormView: View {
.id(EventFormField.attendees)
}
}
.overlay {
if isLogisticsFieldFocused(model.eventFormFocusedField) {
RoundedRectangle(cornerRadius: NativeWebLayoutTokens.radiusDefault)
.stroke(theme.accentColor, lineWidth: 1)
}
}
.id(EventFormField.location)
EventFormCard {
EventFormCard(highlighted: model.eventFormFocusedField == .description) {
DescriptionEditorView(
html: Binding(
get: { draft.description },
Expand All @@ -123,12 +115,6 @@ public struct EventFormView: View {
)
.frame(minHeight: 72, maxHeight: 140)
}
.overlay {
if model.eventFormFocusedField == .description {
RoundedRectangle(cornerRadius: NativeWebLayoutTokens.radiusDefault)
.stroke(theme.accentColor, lineWidth: 1)
}
}
.pageJumpChipAnchor(id: "form-description")
.id(EventFormField.description)

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -184,13 +184,10 @@ public final class TimeGridView: NSView {

private func pulseHeaderLabel(_ header: NSTextField, accent: NSColor) {
header.wantsLayer = true
let shimmer = CABasicAnimation(keyPath: "opacity")
shimmer.fromValue = 1
shimmer.toValue = 0.55
shimmer.duration = 0.7
shimmer.autoreverses = true
shimmer.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
header.layer?.add(shimmer, forKey: "nowCueLabelShimmer")
header.layer?.add(
nowCuePulseAnimation(keyPath: "opacity", from: 1, to: 0.55),
forKey: "nowCueLabelShimmer"
)
if let attributed = header.attributedStringValue.mutableCopy() as? NSMutableAttributedString {
attributed.addAttribute(.foregroundColor, value: accent, range: NSRange(location: 0, length: attributed.length))
header.attributedStringValue = attributed
Expand Down
Loading
Loading