Skip to content
Closed
Show file tree
Hide file tree
Changes from 4 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
39 changes: 29 additions & 10 deletions Sources/Ignite/Elements/NavigationBar.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,12 @@ public struct NavigationBar: HTML {
/// clickable to let users navigate to your homepage.
let logo: (any InlineElement)?

/// An array of items to show in this navigation bar.
/// An array of collapsible items to show in this navigation bar.
let items: [any NavigationItem]

/// An array of permanent elements to show in this navigation bar.
let controls: any HTML

/// The style to use when rendering this bar.
var style = NavigationBarStyle.default

Expand All @@ -78,33 +81,42 @@ public struct NavigationBar: HTML {
) {
self.logo = logo
self.items = []
self.controls = EmptyHTML()
}

/// Creates a new `NavigationBar` instance from the `logo` and
/// `items` provided.
/// Creates a new `NavigationBar` instance from the `logo`,
/// `items`, and `controls` provided.
/// - Parameters:
/// - logo: The logo to use in the top-left edge of your bar.
/// - items: An element builder that returns an array of
/// `NavigationItem` objects.
/// - items: Basic navigation items like `Link` and `Span` that will be
/// collapsed into a hamburger menu at small screen sizes.
/// - controls: Elements positioned at the end of the navigation bar and
/// visible across all screen sizes.
public init(
logo: (any InlineElement)? = nil,
@ElementBuilder<NavigationItem> items: () -> [any NavigationItem]
@ElementBuilder<NavigationItem> items: () -> [any NavigationItem],
@HTMLBuilder controls: () -> some HTML = { EmptyHTML() }
) {
self.logo = logo
self.items = items()
self.controls = controls()
}

/// Creates a new `NavigationBar` instance from the `logo` and
/// `items` provided.
/// Creates a new `NavigationBar` instance from the `logo`,
/// `items`, and `controls` provided.
/// - Parameters:
/// - items: An element builder that returns an array of
/// `NavigationItem` objects.
/// - items: Basic navigation items like `Link` and `Span` that will be
/// collapsed into a hamburger menu at small screen sizes.
/// - controls: Elements positioned at the end of the navigation bar and
/// visible across all screen sizes.
/// - logo: The logo to use in the top-left edge of your bar.
public init(
@ElementBuilder<NavigationItem> items: () -> [any NavigationItem],
@HTMLBuilder controls: () -> some HTML = { EmptyHTML() },
logo: (() -> (any InlineElement))? = nil
) {
self.items = items()
self.controls = controls()
self.logo = logo?()
}

Expand Down Expand Up @@ -158,7 +170,12 @@ public struct NavigationBar: HTML {
Section {
if let logo {
renderLogo(logo)
.class("me-auto me-md-0")
}

AnyHTML(controls)
.class("order-md-last", "ms-auto me-2")

if !items.isEmpty {
renderToggleButton()
renderNavItems()
Expand Down Expand Up @@ -237,6 +254,8 @@ public struct NavigationBar: HTML {
private func renderLogo(_ logo: any InlineElement) -> some InlineElement {
Link(logo, target: "/")
.trimmingMargin()
// To investigate: Label takes up more space than needed
.style(.width, logo.is(Label.self) ? "min-content" : "")
.class("navbar-brand")
}
}
Expand Down
9 changes: 6 additions & 3 deletions Sources/Ignite/Framework/CoreAttributes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -183,14 +183,16 @@ public struct CoreAttributes: Equatable, Sendable, CustomStringConvertible {
/// Appends multiple extra inline CSS styles.
/// - Parameter classes: The inline CSS styles to append.
mutating func append(styles: InlineStyle...) {
let styles = styles.filter { !$0.value.isEmpty }
self.styles.formUnion(styles)
}

/// Appends a single extra inline CSS style.
/// - Parameter style: The style name, e.g. background-color
/// - Parameter value: The style value, e.g. steelblue
mutating func append(style: Property, value: String) {
styles.append(InlineStyle(style, value: value))
guard !value.isEmpty else { return }
styles.append(.init(style, value: value))
}

/// Appends a data attribute.
Expand All @@ -211,6 +213,7 @@ public struct CoreAttributes: Equatable, Sendable, CustomStringConvertible {
/// CSS style properties and their values to be appended.
mutating func append(styles newStyles: some Collection<InlineStyle>) {
var styles = self.styles
let newStyles = newStyles.filter { !$0.value.isEmpty }
styles.formUnion(newStyles)
self.styles = styles
}
Expand All @@ -230,11 +233,11 @@ public struct CoreAttributes: Equatable, Sendable, CustomStringConvertible {
/// Removes specified CSS properties from the element's inline styles.
/// - Parameter properties: Variable number of CSS properties to remove.
mutating func remove(styles properties: Property...) {
var styles = Array(self.styles)
var styles = self.styles
for property in properties {
styles.removeAll(where: { $0.property == property.rawValue })
}
self.styles = OrderedSet(styles)
self.styles = styles
}

/// Retrieves the inline styles for specified CSS properties.
Expand Down
15 changes: 12 additions & 3 deletions Sources/Ignite/Framework/ElementTypes/HTML.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,19 +80,28 @@ extension HTML {
}
}

/// Whether this element represents a specific type.
func `is`(_ elementType: any HTML.Type) -> Bool {
if let anyHTML = body as? AnyHTML {
type(of: anyHTML.wrapped) == elementType
} else {
type(of: body) == elementType
}
}

/// A Boolean value indicating whether this represents `Text`.
var isText: Bool {
body is Text || (body as? AnyHTML)?.wrapped is Text
self.is(Text.self)
}

/// A Boolean value indicating whether this represents `Image`.
var isImage: Bool {
self is Image || (self as? AnyHTML)?.wrapped is Image
self.is(Image.self)
}

/// A Boolean value indicating whether this represents `Section`.
var isSection: Bool {
self is Section || (self as? AnyHTML)?.wrapped is Section
self.is(Section.self)
}

/// A Boolean value indicating whether this represents a textual element.
Expand Down