Skip to content

Commit

Permalink
Move command views into new namespace
Browse files Browse the repository at this point in the history
  • Loading branch information
danielsaidi committed Jan 18, 2024
1 parent e256293 commit ce84a69
Show file tree
Hide file tree
Showing 20 changed files with 449 additions and 390 deletions.
2 changes: 2 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ This release starts moving types and views that relate to other types into the t
### ✨ Features

* `RichTextAlignment.Picker` has a new style parameter.
* `RichTextCommand` is a new namespace for command-related views.

### 🗑️ Deprecations

Expand All @@ -23,6 +24,7 @@ This release starts moving types and views that relate to other types into the t
* `RichTextArgumentReader` deprecates the font name and size functions.
* `RichTextArgumentWriter` deprecates the font name and size functions.
* `RichTextColorPicker` has been renamed to `RichTextColor.Picker`.
* `RichTextCommand` views are now nested within the new `RichTextCommand` type.
* `RichTextComponent` deprecates the font name and size functions.


Expand Down
53 changes: 53 additions & 0 deletions Sources/RichTextKit/Commands/RichTextCommand+ActionButton.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//
// RichTextCommand+ActionButton.swift
// RichTextKit
//
// Created by Daniel Saidi on 2022-12-08.
// Copyright © 2022-2024 Daniel Saidi. All rights reserved.
//

import SwiftUI

public extension RichTextCommand {

/**
This button can be used to trigger any ``RichTextAction``
from a main menu command item.

This button gets ``RichTextContext`` as a focused value.
It will be nil if no view has focus, which will disable
the button altogether.
*/
struct ActionButton: View {

/**
Create a command button.

- Parameters:
- action: The action to trigger.
*/
public init(
action: RichTextAction
) {
self.action = action
}

private let action: RichTextAction

@FocusedValue(\.richTextContext)
private var context: RichTextContext?

public var body: some View {
SwiftUI.Button(action.menuTitle) {
context?.handle(action)
}
.disabled(!canHandle)
.keyboardShortcut(for: action)
.accessibilityLabel(action.title)
}

private var canHandle: Bool {
context?.canHandle(action) ?? false
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//
// RichTextCommand+ActionButtonGroup.swift
// RichTextKit
//
// Created by Daniel Saidi on 2022-12-08.
// Copyright © 2022-2024 Daniel Saidi. All rights reserved.
//

import SwiftUI

public extension RichTextCommand {

/**
This view can be used to render a collection of actions
as main menu command items.
*/
struct ActionButtonGroup: View {

/**
Create a command button group.

- Parameters:
- actions: The actions to trigger.
*/
public init(
actions: [RichTextAction]
) {
self.actions = actions
}

private let actions: [RichTextAction]

public var body: some View {
ForEach(actions) {
ActionButton(action: $0)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// RichTextCommand+AlignmentOptionsGroup.swift
// RichTextKit
//
// Created by Daniel Saidi on 2022-12-20.
// Copyright © 2022-2024 Daniel Saidi. All rights reserved.
//

import SwiftUI

public extension RichTextCommand {

/**
This view renders ``RichTextAlignment`` command options.
*/
struct AlignmentOptionsGroup: View {

public init() {}

public var body: some View {
ActionButtonGroup(
actions: RichTextAlignment.allCases.map {
.setAlignment($0)
}
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//
// RichTextCommand+FontSizeOptionsGroup.swift
// RichTextKit
//
// Created by Daniel Saidi on 2022-12-20.
// Copyright © 2022-2024 Daniel Saidi. All rights reserved.
//

import SwiftUI

public extension RichTextCommand {

/**
This view renders font size command options.
*/
struct FontSizeOptionsGroup: View {

public init() {}

public var body: some View {
ActionButtonGroup(
actions: [
.increaseFontSize(),
.decreaseFontSize()
]
)
}
}
}
46 changes: 46 additions & 0 deletions Sources/RichTextKit/Commands/RichTextCommand+FormatMenu.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//
// RichTextCommand+FormatMenu.swift
// RichTextKit
//
// Created by Daniel Saidi on 2022-12-20.
// Copyright © 2022-2024 Daniel Saidi. All rights reserved.
//

#if iOS || macOS || os(visionOS)
import SwiftUI

public extension RichTextCommand {

/**
This menu can be used to add format-specific options to
the main menu bar.
*/
struct FormatMenu: Commands {

/// Create a rich text format command menu.
public init() {}

@FocusedValue(\.richTextContext)
private var context: RichTextContext?

public var body: some Commands {
CommandMenu(RTKL10n.menuFormat.text) {
Group {
Menu(RTKL10n.menuFont.text) {
StyleOptionsGroup()
Divider()
FontSizeOptionsGroup()
}
Menu(RTKL10n.menuText.text) {
AlignmentOptionsGroup()
}
Menu(RTKL10n.menuIndent.text) {
IndentOptionsGroup()
}
}
.disabled(context == nil)
}
}
}
}
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//
// RichTextCommand+IndentOptionsGroup.swift
// RichTextKit
//
// Created by Daniel Saidi on 2022-12-20.
// Copyright © 2022-2024 Daniel Saidi. All rights reserved.
//

import SwiftUI

public extension RichTextCommand {

/**
This view renders ``RichTextIndent`` command options.
*/
struct IndentOptionsGroup: View {

public var body: some View {
ActionButtonGroup(
actions: [
.increaseIndent(),
.decreaseIndent()
]
)
}
}
}
149 changes: 149 additions & 0 deletions Sources/RichTextKit/Commands/RichTextCommand+ShareMenu.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
//
// RichTextCommand+ShreMenu.swift
// RichTextKit
//
// Created by Daniel Saidi on 2022-12-20.
// Copyright © 2022-2024 Daniel Saidi. All rights reserved.
//

#if iOS || macOS || os(visionOS)
import SwiftUI

public extension RichTextCommand {

/**
This menu can be used to add sharing-related options to
the main menu bar.

The menu will try to add options for share, export, and
print, if applicable to the current platform. Selecting
an option will trigger a corresponding, provided action.

The macOS exclusive `nsSharing` commands require you to
return a share url, after which a command takes care of
the sharing. Also note that the `formatNSSharingAction`
and `pdfNSSharingAction` will only have effect on macOS,
where they will add ``RichTextNSSharingMenu`` options.

Note that a pdf action menu will only be included if it
has a corresponding action. Also note that you must use
`isEnabled: false` to disable the menu, since you can't
add `.disabled` to the command menu.

Setting any action to nil removes a corrsponding option
from the menu.
*/
struct ShareMenu: Commands {

/**
Create a rich text share command menu.
*/
public init(
isEnabled: Bool = true,
shareFormats: [RichTextDataFormat],
exportFormats: [RichTextDataFormat],
formatShareAction: FormatAction? = nil,
pdfShareAction: PdfAction? = nil,
formatNSSharingAction: FormatNSSharingAction? = nil,
pdfNSSharingAction: PdfNSSharingAction? = nil,
formatExportAction: FormatAction? = nil,
pdfExportAction: PdfAction? = nil,
printAction: PrintAction? = nil
) {
self.isEnabled = isEnabled
self.shareFormats = shareFormats
self.exportFormats = exportFormats
self.formatShareAction = formatShareAction
self.pdfShareAction = pdfShareAction
self.formatNSSharingAction = formatNSSharingAction
self.pdfNSSharingAction = pdfNSSharingAction
self.formatExportAction = formatExportAction
self.pdfExportAction = pdfExportAction
self.printAction = printAction
}

public typealias FormatAction = (RichTextDataFormat) -> Void
public typealias FormatNSSharingAction = (RichTextDataFormat) -> URL?
public typealias PdfAction = () -> Void
public typealias PdfNSSharingAction = () -> URL?
public typealias PrintAction = () -> Void

private let shareFormats: [RichTextDataFormat]
private let exportFormats: [RichTextDataFormat]

private let isEnabled: Bool
private let formatShareAction: FormatAction?
private let pdfShareAction: PdfAction?
private let formatNSSharingAction: FormatNSSharingAction?
private let pdfNSSharingAction: PdfNSSharingAction?
private let formatExportAction: FormatAction?
private let pdfExportAction: PdfAction?
private let printAction: PrintAction?

public var body: some Commands {
CommandGroup(replacing: .importExport) {
Group {
nssharingMenu
shareMenu
exportMenu
printButton
}.disabled(!isEnabled)
}
}
}
}

private extension RichTextCommand.ShareMenu {

var hasExportFormats: Bool { !exportFormats.isEmpty }

var hasShareFormats: Bool { !shareFormats.isEmpty }
}

private extension RichTextCommand.ShareMenu {

@ViewBuilder
var exportMenu: some View {
if hasExportFormats, let action = formatExportAction {
RichTextExportMenu(
formats: shareFormats,
formatAction: action,
pdfAction: pdfExportAction
)
}
}

@ViewBuilder
var printButton: some View {
if let action = printAction {
Button(action: action) {
Label(RTKL10n.menuPrint.text, .richTextActionPrint)
}.keyboardShortcut(for: .print)
}
}

@ViewBuilder
var nssharingMenu: some View {
#if macOS
if hasShareFormats, let action = formatNSSharingAction {
RichTextNSSharingMenu(
formats: shareFormats,
formatAction: action,
pdfAction: pdfNSSharingAction
)
}
#endif
}

@ViewBuilder
var shareMenu: some View {
if hasShareFormats, let action = formatShareAction {
RichTextShareMenu(
formats: shareFormats,
formatAction: action,
pdfAction: pdfShareAction
)
}
}
}
#endif
Loading

0 comments on commit ce84a69

Please sign in to comment.