|
@@ -330,8 +330,12 @@ final class ViewController: NSViewController {
|
|
|
private var schedulePageVisibleCount: Int = 0
|
|
private var schedulePageVisibleCount: Int = 0
|
|
|
private let schedulePageBatchSize: Int = 20
|
|
private let schedulePageBatchSize: Int = 20
|
|
|
private let schedulePageCardsPerRow: Int = 3
|
|
private let schedulePageCardsPerRow: Int = 3
|
|
|
- private let schedulePageCardWidth: CGFloat = 240
|
|
|
|
|
- private let schedulePageCardSpacing: CGFloat = 12
|
|
|
|
|
|
|
+ private let schedulePageCardSpacing: CGFloat = 20
|
|
|
|
|
+ private let schedulePageCardHeight: CGFloat = 182
|
|
|
|
|
+ private let schedulePageStackSpacing: CGFloat = 16
|
|
|
|
|
+ private let schedulePageLeadingInset: CGFloat = 28
|
|
|
|
|
+ /// Tighter than leading so cards/filters use full width; overlay scrollbar avoids a dead right gutter.
|
|
|
|
|
+ private let schedulePageTrailingInset: CGFloat = 12
|
|
|
private var schedulePageScrollObservation: NSObjectProtocol?
|
|
private var schedulePageScrollObservation: NSObjectProtocol?
|
|
|
private weak var schedulePageDateHeadingLabel: NSTextField?
|
|
private weak var schedulePageDateHeadingLabel: NSTextField?
|
|
|
private weak var schedulePageFilterDropdown: NSPopUpButton?
|
|
private weak var schedulePageFilterDropdown: NSPopUpButton?
|
|
@@ -1736,20 +1740,25 @@ private extension ViewController {
|
|
|
func makeSchedulePageContent() -> NSView {
|
|
func makeSchedulePageContent() -> NSView {
|
|
|
let panel = NSView()
|
|
let panel = NSView()
|
|
|
panel.translatesAutoresizingMaskIntoConstraints = false
|
|
panel.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
+ panel.userInterfaceLayoutDirection = .leftToRight
|
|
|
|
|
|
|
|
let contentStack = NSStackView()
|
|
let contentStack = NSStackView()
|
|
|
contentStack.translatesAutoresizingMaskIntoConstraints = false
|
|
contentStack.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
+ contentStack.userInterfaceLayoutDirection = .leftToRight
|
|
|
contentStack.orientation = .vertical
|
|
contentStack.orientation = .vertical
|
|
|
- contentStack.spacing = 12
|
|
|
|
|
- contentStack.alignment = .leading
|
|
|
|
|
|
|
+ contentStack.spacing = schedulePageStackSpacing
|
|
|
|
|
+ contentStack.alignment = .width
|
|
|
|
|
|
|
|
- contentStack.addArrangedSubview(schedulePageHeader())
|
|
|
|
|
|
|
+ let header = schedulePageHeader()
|
|
|
|
|
+ contentStack.addArrangedSubview(header)
|
|
|
|
|
|
|
|
let heading = textLabel(schedulePageInitialHeadingText(), font: typography.dateHeading, color: palette.textSecondary)
|
|
let heading = textLabel(schedulePageInitialHeadingText(), font: typography.dateHeading, color: palette.textSecondary)
|
|
|
|
|
+ heading.alignment = .left
|
|
|
schedulePageDateHeadingLabel = heading
|
|
schedulePageDateHeadingLabel = heading
|
|
|
contentStack.addArrangedSubview(heading)
|
|
contentStack.addArrangedSubview(heading)
|
|
|
|
|
|
|
|
let rangeError = textLabel("", font: NSFont.systemFont(ofSize: 12, weight: .semibold), color: .systemRed)
|
|
let rangeError = textLabel("", font: NSFont.systemFont(ofSize: 12, weight: .semibold), color: .systemRed)
|
|
|
|
|
+ rangeError.alignment = .left
|
|
|
rangeError.isHidden = true
|
|
rangeError.isHidden = true
|
|
|
schedulePageRangeErrorLabel = rangeError
|
|
schedulePageRangeErrorLabel = rangeError
|
|
|
contentStack.addArrangedSubview(rangeError)
|
|
contentStack.addArrangedSubview(rangeError)
|
|
@@ -1760,11 +1769,14 @@ private extension ViewController {
|
|
|
panel.addSubview(contentStack)
|
|
panel.addSubview(contentStack)
|
|
|
|
|
|
|
|
NSLayoutConstraint.activate([
|
|
NSLayoutConstraint.activate([
|
|
|
- contentStack.leadingAnchor.constraint(equalTo: panel.leadingAnchor, constant: 28),
|
|
|
|
|
- contentStack.trailingAnchor.constraint(equalTo: panel.trailingAnchor, constant: -28),
|
|
|
|
|
|
|
+ contentStack.leftAnchor.constraint(equalTo: panel.leftAnchor, constant: schedulePageLeadingInset),
|
|
|
|
|
+ contentStack.rightAnchor.constraint(equalTo: panel.rightAnchor, constant: -schedulePageTrailingInset),
|
|
|
contentStack.topAnchor.constraint(equalTo: panel.topAnchor, constant: 6),
|
|
contentStack.topAnchor.constraint(equalTo: panel.topAnchor, constant: 6),
|
|
|
- cardsContainer.leadingAnchor.constraint(equalTo: contentStack.leadingAnchor),
|
|
|
|
|
- cardsContainer.bottomAnchor.constraint(equalTo: panel.bottomAnchor, constant: -16)
|
|
|
|
|
|
|
+ contentStack.bottomAnchor.constraint(equalTo: panel.bottomAnchor, constant: -16),
|
|
|
|
|
+ header.widthAnchor.constraint(equalTo: contentStack.widthAnchor),
|
|
|
|
|
+ heading.widthAnchor.constraint(equalTo: contentStack.widthAnchor),
|
|
|
|
|
+ rangeError.widthAnchor.constraint(equalTo: contentStack.widthAnchor),
|
|
|
|
|
+ cardsContainer.widthAnchor.constraint(equalTo: contentStack.widthAnchor)
|
|
|
])
|
|
])
|
|
|
|
|
|
|
|
Task { [weak self] in
|
|
Task { [weak self] in
|
|
@@ -2658,37 +2670,81 @@ private extension ViewController {
|
|
|
private func schedulePageHeader() -> NSView {
|
|
private func schedulePageHeader() -> NSView {
|
|
|
let container = NSStackView()
|
|
let container = NSStackView()
|
|
|
container.translatesAutoresizingMaskIntoConstraints = false
|
|
container.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
+ container.userInterfaceLayoutDirection = .leftToRight
|
|
|
container.orientation = .vertical
|
|
container.orientation = .vertical
|
|
|
- container.spacing = 10
|
|
|
|
|
- container.alignment = .leading
|
|
|
|
|
|
|
+ container.spacing = 14
|
|
|
|
|
+ container.alignment = .width
|
|
|
|
|
+
|
|
|
|
|
+ let titleRow = NSStackView()
|
|
|
|
|
+ titleRow.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
+ titleRow.userInterfaceLayoutDirection = .leftToRight
|
|
|
|
|
+ titleRow.orientation = .horizontal
|
|
|
|
|
+ titleRow.alignment = .centerY
|
|
|
|
|
+ titleRow.distribution = .fill
|
|
|
|
|
+ titleRow.spacing = 0
|
|
|
|
|
|
|
|
let titleLabel = textLabel("Schedule", font: typography.pageTitle, color: palette.textPrimary)
|
|
let titleLabel = textLabel("Schedule", font: typography.pageTitle, color: palette.textPrimary)
|
|
|
- container.addArrangedSubview(titleLabel)
|
|
|
|
|
|
|
+ titleLabel.alignment = .left
|
|
|
|
|
+ titleLabel.userInterfaceLayoutDirection = .leftToRight
|
|
|
|
|
+ titleLabel.maximumNumberOfLines = 1
|
|
|
|
|
+ titleLabel.lineBreakMode = .byTruncatingTail
|
|
|
|
|
+ titleLabel.setContentHuggingPriority(.required, for: .horizontal)
|
|
|
|
|
+ titleLabel.setContentCompressionResistancePriority(.required, for: .horizontal)
|
|
|
|
|
+
|
|
|
|
|
+ let titleRowSpacer = NSView()
|
|
|
|
|
+ titleRowSpacer.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
+ titleRowSpacer.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
|
|
|
+ titleRowSpacer.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
|
|
|
|
+
|
|
|
|
|
+ titleRow.addArrangedSubview(titleLabel)
|
|
|
|
|
+ titleRow.addArrangedSubview(titleRowSpacer)
|
|
|
|
|
+ container.addArrangedSubview(titleRow)
|
|
|
|
|
|
|
|
let filterRow = NSStackView()
|
|
let filterRow = NSStackView()
|
|
|
filterRow.translatesAutoresizingMaskIntoConstraints = false
|
|
filterRow.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
+ filterRow.userInterfaceLayoutDirection = .leftToRight
|
|
|
filterRow.orientation = .horizontal
|
|
filterRow.orientation = .horizontal
|
|
|
filterRow.alignment = .centerY
|
|
filterRow.alignment = .centerY
|
|
|
- filterRow.spacing = 10
|
|
|
|
|
|
|
+ filterRow.spacing = 18
|
|
|
|
|
+ filterRow.distribution = .fill
|
|
|
|
|
|
|
|
let filterDropdown = makeSchedulePageFilterDropdown()
|
|
let filterDropdown = makeSchedulePageFilterDropdown()
|
|
|
schedulePageFilterDropdown = filterDropdown
|
|
schedulePageFilterDropdown = filterDropdown
|
|
|
filterRow.addArrangedSubview(filterDropdown)
|
|
filterRow.addArrangedSubview(filterDropdown)
|
|
|
|
|
+ filterRow.setCustomSpacing(20, after: filterDropdown)
|
|
|
|
|
|
|
|
let fromPicker = makeScheduleDatePicker(date: schedulePageFromDate)
|
|
let fromPicker = makeScheduleDatePicker(date: schedulePageFromDate)
|
|
|
schedulePageFromDatePicker = fromPicker
|
|
schedulePageFromDatePicker = fromPicker
|
|
|
filterRow.addArrangedSubview(fromPicker)
|
|
filterRow.addArrangedSubview(fromPicker)
|
|
|
|
|
+ filterRow.setCustomSpacing(16, after: fromPicker)
|
|
|
|
|
|
|
|
let toPicker = makeScheduleDatePicker(date: schedulePageToDate)
|
|
let toPicker = makeScheduleDatePicker(date: schedulePageToDate)
|
|
|
schedulePageToDatePicker = toPicker
|
|
schedulePageToDatePicker = toPicker
|
|
|
filterRow.addArrangedSubview(toPicker)
|
|
filterRow.addArrangedSubview(toPicker)
|
|
|
|
|
+ NSLayoutConstraint.activate([
|
|
|
|
|
+ fromPicker.widthAnchor.constraint(equalTo: toPicker.widthAnchor),
|
|
|
|
|
+ fromPicker.widthAnchor.constraint(greaterThanOrEqualToConstant: 152)
|
|
|
|
|
+ ])
|
|
|
|
|
|
|
|
- filterRow.addArrangedSubview(makeSchedulePagePillButton(title: "Apply", action: #selector(schedulePageApplyDateRangePressed(_:))))
|
|
|
|
|
- filterRow.addArrangedSubview(makeSchedulePagePillButton(title: "Reset", action: #selector(schedulePageResetFiltersPressed(_:))))
|
|
|
|
|
|
|
+ let filterRowSpacer = NSView()
|
|
|
|
|
+ filterRowSpacer.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
+ filterRowSpacer.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
|
|
|
+ filterRowSpacer.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
|
|
|
|
+ filterRow.addArrangedSubview(filterRowSpacer)
|
|
|
|
|
+
|
|
|
|
|
+ let applyButton = makeSchedulePagePillButton(title: "Apply", action: #selector(schedulePageApplyDateRangePressed(_:)))
|
|
|
|
|
+ filterRow.addArrangedSubview(applyButton)
|
|
|
|
|
+ filterRow.setCustomSpacing(22, after: applyButton)
|
|
|
|
|
+ let resetButton = makeSchedulePagePillButton(title: "Reset", action: #selector(schedulePageResetFiltersPressed(_:)))
|
|
|
|
|
+ filterRow.addArrangedSubview(resetButton)
|
|
|
|
|
+ filterRow.setCustomSpacing(22, after: resetButton)
|
|
|
filterRow.addArrangedSubview(makeScheduleRefreshButton())
|
|
filterRow.addArrangedSubview(makeScheduleRefreshButton())
|
|
|
|
|
|
|
|
container.addArrangedSubview(filterRow)
|
|
container.addArrangedSubview(filterRow)
|
|
|
- container.widthAnchor.constraint(greaterThanOrEqualToConstant: 780).isActive = true
|
|
|
|
|
|
|
+ NSLayoutConstraint.activate([
|
|
|
|
|
+ titleRow.widthAnchor.constraint(equalTo: container.widthAnchor),
|
|
|
|
|
+ filterRow.widthAnchor.constraint(equalTo: container.widthAnchor)
|
|
|
|
|
+ ])
|
|
|
refreshSchedulePageDateFilterUI()
|
|
refreshSchedulePageDateFilterUI()
|
|
|
return container
|
|
return container
|
|
|
}
|
|
}
|
|
@@ -2710,7 +2766,7 @@ private extension ViewController {
|
|
|
button.target = self
|
|
button.target = self
|
|
|
button.action = #selector(schedulePageFilterDropdownChanged(_:))
|
|
button.action = #selector(schedulePageFilterDropdownChanged(_:))
|
|
|
button.heightAnchor.constraint(equalToConstant: 34).isActive = true
|
|
button.heightAnchor.constraint(equalToConstant: 34).isActive = true
|
|
|
- button.widthAnchor.constraint(equalToConstant: 190).isActive = true
|
|
|
|
|
|
|
+ button.widthAnchor.constraint(equalToConstant: 228).isActive = true
|
|
|
|
|
|
|
|
button.removeAllItems()
|
|
button.removeAllItems()
|
|
|
button.addItems(withTitles: ["All", "Today", "This week", "This month", "Custom range"])
|
|
button.addItems(withTitles: ["All", "Today", "This week", "This month", "Custom range"])
|
|
@@ -2742,7 +2798,7 @@ private extension ViewController {
|
|
|
picker.dateValue = date
|
|
picker.dateValue = date
|
|
|
picker.font = typography.filterText
|
|
picker.font = typography.filterText
|
|
|
picker.heightAnchor.constraint(equalToConstant: 34).isActive = true
|
|
picker.heightAnchor.constraint(equalToConstant: 34).isActive = true
|
|
|
- picker.widthAnchor.constraint(equalToConstant: 156).isActive = true
|
|
|
|
|
|
|
+ picker.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
|
picker.target = self
|
|
picker.target = self
|
|
|
picker.action = #selector(schedulePageDatePickerChanged(_:))
|
|
picker.action = #selector(schedulePageDatePickerChanged(_:))
|
|
|
return picker
|
|
return picker
|
|
@@ -2752,7 +2808,7 @@ private extension ViewController {
|
|
|
let button = makeSchedulePillButton(title: title)
|
|
let button = makeSchedulePillButton(title: title)
|
|
|
button.target = self
|
|
button.target = self
|
|
|
button.action = action
|
|
button.action = action
|
|
|
- button.widthAnchor.constraint(equalToConstant: 92).isActive = true
|
|
|
|
|
|
|
+ button.widthAnchor.constraint(equalToConstant: 100).isActive = true
|
|
|
return button
|
|
return button
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -2764,24 +2820,27 @@ private extension ViewController {
|
|
|
|
|
|
|
|
let wrapper = NSView()
|
|
let wrapper = NSView()
|
|
|
wrapper.translatesAutoresizingMaskIntoConstraints = false
|
|
wrapper.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
+ wrapper.userInterfaceLayoutDirection = .leftToRight
|
|
|
|
|
|
|
|
let scroll = NSScrollView()
|
|
let scroll = NSScrollView()
|
|
|
scroll.translatesAutoresizingMaskIntoConstraints = false
|
|
scroll.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
+ scroll.userInterfaceLayoutDirection = .leftToRight
|
|
|
scroll.drawsBackground = false
|
|
scroll.drawsBackground = false
|
|
|
scroll.hasHorizontalScroller = false
|
|
scroll.hasHorizontalScroller = false
|
|
|
scroll.hasVerticalScroller = true
|
|
scroll.hasVerticalScroller = true
|
|
|
scroll.autohidesScrollers = true
|
|
scroll.autohidesScrollers = true
|
|
|
scroll.borderType = .noBorder
|
|
scroll.borderType = .noBorder
|
|
|
- let viewportWidth = (schedulePageCardWidth * CGFloat(schedulePageCardsPerRow)) + (schedulePageCardSpacing * CGFloat(schedulePageCardsPerRow - 1))
|
|
|
|
|
- scroll.widthAnchor.constraint(equalToConstant: viewportWidth).isActive = true
|
|
|
|
|
|
|
+ scroll.scrollerStyle = .overlay
|
|
|
|
|
+ scroll.automaticallyAdjustsContentInsets = false
|
|
|
schedulePageCardsScrollView = scroll
|
|
schedulePageCardsScrollView = scroll
|
|
|
wrapper.addSubview(scroll)
|
|
wrapper.addSubview(scroll)
|
|
|
|
|
|
|
|
let stack = NSStackView()
|
|
let stack = NSStackView()
|
|
|
stack.translatesAutoresizingMaskIntoConstraints = false
|
|
stack.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
+ stack.userInterfaceLayoutDirection = .leftToRight
|
|
|
stack.orientation = .vertical
|
|
stack.orientation = .vertical
|
|
|
- stack.spacing = 12
|
|
|
|
|
- stack.alignment = .leading
|
|
|
|
|
|
|
+ stack.spacing = schedulePageCardSpacing
|
|
|
|
|
+ stack.alignment = .width
|
|
|
schedulePageCardsStack = stack
|
|
schedulePageCardsStack = stack
|
|
|
scroll.documentView = stack
|
|
scroll.documentView = stack
|
|
|
|
|
|
|
@@ -3035,16 +3094,21 @@ private extension ViewController {
|
|
|
return wrapper
|
|
return wrapper
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- func scheduleCard(meeting: ScheduledMeeting) -> NSView {
|
|
|
|
|
|
|
+ func scheduleCard(meeting: ScheduledMeeting, useFlexibleWidth: Bool = false, contentHeight: CGFloat = 150) -> NSView {
|
|
|
let cardWidth: CGFloat = 240
|
|
let cardWidth: CGFloat = 240
|
|
|
|
|
|
|
|
let card = roundedContainer(cornerRadius: 12, color: palette.sectionCard)
|
|
let card = roundedContainer(cornerRadius: 12, color: palette.sectionCard)
|
|
|
styleSurface(card, borderColor: palette.inputBorder, borderWidth: 1, shadow: true)
|
|
styleSurface(card, borderColor: palette.inputBorder, borderWidth: 1, shadow: true)
|
|
|
card.translatesAutoresizingMaskIntoConstraints = false
|
|
card.translatesAutoresizingMaskIntoConstraints = false
|
|
|
- card.widthAnchor.constraint(equalToConstant: cardWidth).isActive = true
|
|
|
|
|
- card.heightAnchor.constraint(equalToConstant: 150).isActive = true
|
|
|
|
|
- card.setContentHuggingPriority(.required, for: .horizontal)
|
|
|
|
|
- card.setContentCompressionResistancePriority(.required, for: .horizontal)
|
|
|
|
|
|
|
+ card.heightAnchor.constraint(equalToConstant: contentHeight).isActive = true
|
|
|
|
|
+ if useFlexibleWidth {
|
|
|
|
|
+ card.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
|
|
|
+ card.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ card.widthAnchor.constraint(equalToConstant: cardWidth).isActive = true
|
|
|
|
|
+ card.setContentHuggingPriority(.required, for: .horizontal)
|
|
|
|
|
+ card.setContentCompressionResistancePriority(.required, for: .horizontal)
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
let icon = roundedContainer(cornerRadius: 8, color: palette.meetingBadge)
|
|
let icon = roundedContainer(cornerRadius: 8, color: palette.meetingBadge)
|
|
|
icon.translatesAutoresizingMaskIntoConstraints = false
|
|
icon.translatesAutoresizingMaskIntoConstraints = false
|
|
@@ -3089,7 +3153,7 @@ private extension ViewController {
|
|
|
card.addSubview(time)
|
|
card.addSubview(time)
|
|
|
card.addSubview(duration)
|
|
card.addSubview(duration)
|
|
|
|
|
|
|
|
- NSLayoutConstraint.activate([
|
|
|
|
|
|
|
+ var titleConstraints: [NSLayoutConstraint] = [
|
|
|
icon.leadingAnchor.constraint(equalTo: card.leadingAnchor, constant: 10),
|
|
icon.leadingAnchor.constraint(equalTo: card.leadingAnchor, constant: 10),
|
|
|
icon.topAnchor.constraint(equalTo: card.topAnchor, constant: 10),
|
|
icon.topAnchor.constraint(equalTo: card.topAnchor, constant: 10),
|
|
|
|
|
|
|
@@ -3098,9 +3162,12 @@ private extension ViewController {
|
|
|
|
|
|
|
|
title.leadingAnchor.constraint(equalTo: icon.trailingAnchor, constant: 6),
|
|
title.leadingAnchor.constraint(equalTo: icon.trailingAnchor, constant: 6),
|
|
|
title.centerYAnchor.constraint(equalTo: icon.centerYAnchor),
|
|
title.centerYAnchor.constraint(equalTo: icon.centerYAnchor),
|
|
|
- title.trailingAnchor.constraint(lessThanOrEqualTo: dayChip.leadingAnchor, constant: -8),
|
|
|
|
|
- title.widthAnchor.constraint(lessThanOrEqualToConstant: 130),
|
|
|
|
|
-
|
|
|
|
|
|
|
+ title.trailingAnchor.constraint(lessThanOrEqualTo: dayChip.leadingAnchor, constant: -8)
|
|
|
|
|
+ ]
|
|
|
|
|
+ if !useFlexibleWidth {
|
|
|
|
|
+ titleConstraints.append(title.widthAnchor.constraint(lessThanOrEqualToConstant: 130))
|
|
|
|
|
+ }
|
|
|
|
|
+ NSLayoutConstraint.activate(titleConstraints + [
|
|
|
subtitle.leadingAnchor.constraint(equalTo: card.leadingAnchor, constant: 10),
|
|
subtitle.leadingAnchor.constraint(equalTo: card.leadingAnchor, constant: 10),
|
|
|
subtitle.topAnchor.constraint(equalTo: icon.bottomAnchor, constant: 10),
|
|
subtitle.topAnchor.constraint(equalTo: icon.bottomAnchor, constant: 10),
|
|
|
subtitle.trailingAnchor.constraint(lessThanOrEqualTo: card.trailingAnchor, constant: -10),
|
|
subtitle.trailingAnchor.constraint(lessThanOrEqualTo: card.trailingAnchor, constant: -10),
|
|
@@ -3119,10 +3186,15 @@ private extension ViewController {
|
|
|
hit.isBordered = false
|
|
hit.isBordered = false
|
|
|
hit.bezelStyle = .regularSquare
|
|
hit.bezelStyle = .regularSquare
|
|
|
hit.identifier = NSUserInterfaceItemIdentifier(meeting.meetURL.absoluteString)
|
|
hit.identifier = NSUserInterfaceItemIdentifier(meeting.meetURL.absoluteString)
|
|
|
- hit.widthAnchor.constraint(equalToConstant: cardWidth).isActive = true
|
|
|
|
|
- hit.heightAnchor.constraint(equalToConstant: 150).isActive = true
|
|
|
|
|
- hit.setContentHuggingPriority(.required, for: .horizontal)
|
|
|
|
|
- hit.setContentCompressionResistancePriority(.required, for: .horizontal)
|
|
|
|
|
|
|
+ hit.heightAnchor.constraint(equalToConstant: contentHeight).isActive = true
|
|
|
|
|
+ if useFlexibleWidth {
|
|
|
|
|
+ hit.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
|
|
|
+ hit.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ hit.widthAnchor.constraint(equalToConstant: cardWidth).isActive = true
|
|
|
|
|
+ hit.setContentHuggingPriority(.required, for: .horizontal)
|
|
|
|
|
+ hit.setContentCompressionResistancePriority(.required, for: .horizontal)
|
|
|
|
|
+ }
|
|
|
hit.addSubview(card)
|
|
hit.addSubview(card)
|
|
|
NSLayoutConstraint.activate([
|
|
NSLayoutConstraint.activate([
|
|
|
card.leadingAnchor.constraint(equalTo: hit.leadingAnchor),
|
|
card.leadingAnchor.constraint(equalTo: hit.leadingAnchor),
|
|
@@ -4437,9 +4509,8 @@ private extension ViewController {
|
|
|
if visibleMeetings.isEmpty {
|
|
if visibleMeetings.isEmpty {
|
|
|
let empty = roundedContainer(cornerRadius: 10, color: palette.sectionCard)
|
|
let empty = roundedContainer(cornerRadius: 10, color: palette.sectionCard)
|
|
|
empty.translatesAutoresizingMaskIntoConstraints = false
|
|
empty.translatesAutoresizingMaskIntoConstraints = false
|
|
|
- empty.heightAnchor.constraint(equalToConstant: 120).isActive = true
|
|
|
|
|
- let viewportWidth = (schedulePageCardWidth * CGFloat(schedulePageCardsPerRow)) + (schedulePageCardSpacing * CGFloat(schedulePageCardsPerRow - 1))
|
|
|
|
|
- empty.widthAnchor.constraint(equalToConstant: viewportWidth).isActive = true
|
|
|
|
|
|
|
+ empty.heightAnchor.constraint(equalToConstant: 140).isActive = true
|
|
|
|
|
+ empty.widthAnchor.constraint(equalTo: stack.widthAnchor).isActive = true
|
|
|
styleSurface(empty, borderColor: palette.inputBorder, borderWidth: 1, shadow: false)
|
|
styleSurface(empty, borderColor: palette.inputBorder, borderWidth: 1, shadow: false)
|
|
|
let label = textLabel(googleOAuth.loadTokens() == nil ? "Connect to load schedule" : "No meetings for selected filters", font: typography.cardSubtitle, color: palette.textSecondary)
|
|
let label = textLabel(googleOAuth.loadTokens() == nil ? "Connect to load schedule" : "No meetings for selected filters", font: typography.cardSubtitle, color: palette.textSecondary)
|
|
|
label.translatesAutoresizingMaskIntoConstraints = false
|
|
label.translatesAutoresizingMaskIntoConstraints = false
|
|
@@ -4456,24 +4527,17 @@ private extension ViewController {
|
|
|
while index < visibleMeetings.count {
|
|
while index < visibleMeetings.count {
|
|
|
let row = NSStackView()
|
|
let row = NSStackView()
|
|
|
row.translatesAutoresizingMaskIntoConstraints = false
|
|
row.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
+ row.userInterfaceLayoutDirection = .leftToRight
|
|
|
row.orientation = .horizontal
|
|
row.orientation = .horizontal
|
|
|
row.alignment = .top
|
|
row.alignment = .top
|
|
|
row.spacing = schedulePageCardSpacing
|
|
row.spacing = schedulePageCardSpacing
|
|
|
- row.distribution = .fill
|
|
|
|
|
|
|
+ row.distribution = .fillEqually
|
|
|
let rowEnd = min(index + schedulePageCardsPerRow, visibleMeetings.count)
|
|
let rowEnd = min(index + schedulePageCardsPerRow, visibleMeetings.count)
|
|
|
for meeting in visibleMeetings[index..<rowEnd] {
|
|
for meeting in visibleMeetings[index..<rowEnd] {
|
|
|
- row.addArrangedSubview(scheduleCard(meeting: meeting))
|
|
|
|
|
- }
|
|
|
|
|
- if rowEnd - index < schedulePageCardsPerRow {
|
|
|
|
|
- for _ in 0..<(schedulePageCardsPerRow - (rowEnd - index)) {
|
|
|
|
|
- let spacer = NSView()
|
|
|
|
|
- spacer.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
- spacer.widthAnchor.constraint(equalToConstant: schedulePageCardWidth).isActive = true
|
|
|
|
|
- spacer.heightAnchor.constraint(equalToConstant: 150).isActive = true
|
|
|
|
|
- row.addArrangedSubview(spacer)
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ row.addArrangedSubview(scheduleCard(meeting: meeting, useFlexibleWidth: true, contentHeight: schedulePageCardHeight))
|
|
|
}
|
|
}
|
|
|
stack.addArrangedSubview(row)
|
|
stack.addArrangedSubview(row)
|
|
|
|
|
+ row.widthAnchor.constraint(equalTo: stack.widthAnchor).isActive = true
|
|
|
index = rowEnd
|
|
index = rowEnd
|
|
|
}
|
|
}
|
|
|
|
|
|