Skip to content

Commit dba7594

Browse files
author
Reed Es
committed
new, cleaner hover implementation #35
1 parent 606dc59 commit dba7594

40 files changed

+69
-160
lines changed

README.md

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ macOS | iOS
1919
* Support for single-select, multi-select, or no selection
2020
* Option to sort by column, with indicators and concise syntax
2121
* Option to specify a row background and/or overlay
22-
* On macOS, option for a hovering highlight, to indicate which row the mouse is over
22+
* On macOS, option for hover events, such as to highlight row under the mouse cursor
2323
* MINIMAL use of View erasure (i.e., use of `AnyView`), which can impact scalability and performance\*\*
2424
* No external dependencies!
2525

@@ -308,22 +308,33 @@ private func rowOverlay(fruit: Fruit) -> some View {
308308
}
309309
```
310310

311-
## Hover Effect
311+
## Hover Events
312312

313-
Available for macOS. It's enabled by default using the system's accent color.
314-
315-
When using a row background or overlay on macOS, you might wish to disable the hover effect.
313+
For macOS only, you can capture hover events, typically to highlight the row under the mouse cursor.
316314

317315
```swift
316+
@State private var hovered: Fruit.ID? = nil
317+
318318
var body: some View {
319-
TablerList(.init(hoverColor: .clear),
319+
TablerList(.init(onHover: hoverAction),
320320
header: header,
321321
row: row,
322-
rowBackground: rowBackgroundAction,
322+
rowBackground: rowBackground,
323323
results: fruits)
324324
}
325+
326+
private func rowBackground(fruit: Fruit) -> some View {
327+
RoundedRectangle(cornerRadius: 5)
328+
.fill(Color.accentColor.opacity(hovered == fruit.id ? 0.2 : 0.0))
329+
}
330+
331+
private func hoverAction(fruit: Fruit, isHovered: Bool) {
332+
if isHovered { hovered = fruit.id } else { hovered = nil }
333+
}
325334
```
326335

336+
To coordinate hover with other backgrounds, such as for selection on **Stack** tables, see the demo apps.
337+
327338
## Headless Tables
328339

329340
Where you don't want a header, simply omit it from the declaration of the table:
@@ -393,7 +404,7 @@ List configuration is optional.
393404
- `canMove: CanMove<Element>` - with a default of `{ _ in true }`, allowing any row to move (if `onMove` defined)
394405
- `onMove: OnMove<Element>?` - with a default of `nil`, prohibiting any move
395406
- `filter: Filter?` - with a default of `nil`, indicating no filtering
396-
- `hoverColor: Color` - per Base defaults
407+
- `onHover: (Element, Bool) -> Void` - defaults to `{ _,_ in }`
397408
- `tablePadding: EdgeInsets` - per Base defaults
398409
- `sortIndicatorForward: AnyView` - per Base defaults
399410
- `sortIndicatorReverse: AnyView` - per Base defaults
@@ -409,7 +420,7 @@ Stack configuration is optional.
409420
- `headerSpacing: CGFloat` - Stack-specific default, varies by platform
410421
- `rowSpacing: CGFloat` - Stack-specific default, varies by platform
411422
- `filter: Filter?` - with a default of `nil`, indicating no filtering
412-
- `hoverColor: Color` - per Base defaults
423+
- `onHover: (Element, Bool) -> Void` - defaults to `{ _,_ in }`
413424
- `tablePadding: EdgeInsets` - per Stack defaults
414425
- `sortIndicatorForward: AnyView` - per Base defaults
415426
- `sortIndicatorReverse: AnyView` - per Base defaults
@@ -427,7 +438,7 @@ Grid configuration is required, where you supply a `GridItem` array.
427438
- `headerSpacing: CGFloat` - Grid-specific default, varies by platform
428439
- `rowSpacing: CGFloat` - Grid-specific default, varies by platform
429440
- `filter: Filter?` - with a default of `nil`, indicating no filtering
430-
- `hoverColor: Color` - per Base defaults
441+
- `onHover: (Element, Bool) -> Void` - defaults to `{ _,_ in }`
431442
- `tablePadding: EdgeInsets` - Grid-specific default, varies by platform
432443
- `sortIndicatorForward: AnyView` - per Base defaults
433444
- `sortIndicatorReverse: AnyView` - per Base defaults

Sources/Grid/Internal/GridItemMod.swift

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,16 @@ struct GridItemMod<Element>: ViewModifier
2222
where Element: Identifiable
2323
{
2424
typealias Config = TablerGridConfig<Element>
25-
typealias Hovered = Element.ID?
2625

2726
let config: Config
2827
let element: Element
29-
@Binding var hovered: Hovered
3028

3129
func body(content: Content) -> some View {
3230
content
3331
.padding(config.itemPadding)
3432

3533
#if os(macOS) || targetEnvironment(macCatalyst)
36-
.onHover { if $0 { hovered = element.id } else { hovered = nil } }
37-
//.frame(maxWidth: .infinity) // NOTE this centers the grid item
38-
.background(hovered == element.id ? config.hoverColor : Color.clear)
34+
.onHover(perform: { config.onHover(element, $0) })
3935
#endif
4036
}
4137
}

Sources/Grid/Internal/GridItemMod1.swift

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,10 @@ struct GridItemMod1<Element>: ViewModifier
2323
where Element: Identifiable
2424
{
2525
typealias Config = TablerGridConfig<Element>
26-
typealias Hovered = Element.ID?
2726
typealias Selected = Element.ID?
2827

2928
let config: Config
3029
let element: Element
31-
@Binding var hovered: Hovered
3230
@Binding var selected: Selected
3331

3432
func body(content: Content) -> some View {
@@ -44,11 +42,9 @@ struct GridItemMod1<Element>: ViewModifier
4442
selected = element.id
4543
}
4644
}
47-
45+
4846
#if os(macOS) || targetEnvironment(macCatalyst)
49-
.onHover { if $0 { hovered = element.id } else { hovered = nil } }
50-
//.frame(maxWidth: .infinity) // NOTE this centers the grid item
51-
.background(hovered == element.id ? config.hoverColor : Color.clear)
47+
.onHover(perform: { config.onHover(element, $0) })
5248
#endif
5349
}
5450
}

Sources/Grid/Internal/GridItemModM.swift

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,10 @@ struct GridItemModM<Element>: ViewModifier
2323
where Element: Identifiable
2424
{
2525
typealias Config = TablerGridConfig<Element>
26-
typealias Hovered = Element.ID?
2726
typealias Selected = Set<Element.ID>
2827

2928
let config: Config
3029
let element: Element
31-
@Binding var hovered: Hovered
3230
@Binding var selected: Selected
3331

3432
func body(content: Content) -> some View {
@@ -44,11 +42,9 @@ struct GridItemModM<Element>: ViewModifier
4442
selected.insert(element.id)
4543
}
4644
}
47-
45+
4846
#if os(macOS) || targetEnvironment(macCatalyst)
49-
.onHover { if $0 { hovered = element.id } else { hovered = nil } }
50-
//.frame(maxWidth: .infinity) // NOTE this centers the grid item
51-
.background(hovered == element.id ? config.hoverColor : Color.clear)
47+
.onHover(perform: { config.onHover(element, $0) })
5248
#endif
5349
}
5450
}

Sources/Grid/TablerGrid.swift

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ public struct TablerGrid<Element, Header, Row, RowBack, RowOver, Results>: View
3030
{
3131
public typealias Config = TablerGridConfig<Element>
3232
public typealias Context = TablerContext<Element>
33-
public typealias Hovered = Element.ID?
3433
public typealias HeaderContent = (Binding<Context>) -> Header
3534
public typealias RowContent = (Element) -> Row
3635
public typealias RowBackground = (Element) -> RowBack
@@ -62,7 +61,6 @@ public struct TablerGrid<Element, Header, Row, RowBack, RowOver, Results>: View
6261

6362
// MARK: Locals
6463

65-
@State private var hovered: Hovered = nil
6664
@State private var context: Context
6765

6866
// MARK: Views
@@ -73,8 +71,7 @@ public struct TablerGrid<Element, Header, Row, RowBack, RowOver, Results>: View
7371
ForEach(results.filter(config.filter ?? { _ in true })) { element in
7472
rowContent(element)
7573
.modifier(GridItemMod(config: config,
76-
element: element,
77-
hovered: $hovered))
74+
element: element))
7875
.background(rowBackground(element))
7976
.overlay(rowOverlay(element))
8077
}

Sources/Grid/TablerGrid1.swift

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ public struct TablerGrid1<Element, Header, Row, RowBack, RowOver, Results>: View
3030
{
3131
public typealias Config = TablerGridConfig<Element>
3232
public typealias Context = TablerContext<Element>
33-
public typealias Hovered = Element.ID?
3433
public typealias HeaderContent = (Binding<Context>) -> Header
3534
public typealias RowContent = (Element) -> Row
3635
public typealias RowBackground = (Element) -> RowBack
@@ -67,7 +66,6 @@ public struct TablerGrid1<Element, Header, Row, RowBack, RowOver, Results>: View
6766

6867
// MARK: Locals
6968

70-
@State private var hovered: Hovered = nil
7169
@State private var context: Context
7270

7371
// MARK: Views
@@ -79,8 +77,7 @@ public struct TablerGrid1<Element, Header, Row, RowBack, RowOver, Results>: View
7977
rowContent(element)
8078
.modifier(GridItemMod1(config: config,
8179
element: element,
82-
hovered: $hovered,
83-
selected: $selected))
80+
selected: $selected))
8481
.background(rowBackground(element))
8582
.overlay(rowOverlay(element))
8683
}

Sources/Grid/TablerGrid1B.swift

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ public struct TablerGrid1B<Element, Header, Row, RowBack, RowOver, Results>: Vie
3131
{
3232
public typealias Config = TablerGridConfig<Element>
3333
public typealias Context = TablerContext<Element>
34-
public typealias Hovered = Element.ID?
3534
public typealias HeaderContent = (Binding<Context>) -> Header
3635
public typealias RowContent = (Binding<Element>) -> Row
3736
public typealias RowBackground = (Element) -> RowBack
@@ -68,7 +67,6 @@ public struct TablerGrid1B<Element, Header, Row, RowBack, RowOver, Results>: Vie
6867

6968
// MARK: Locals
7069

71-
@State private var hovered: Hovered = nil
7270
@State private var context: Context
7371

7472
// MARK: Views
@@ -80,8 +78,7 @@ public struct TablerGrid1B<Element, Header, Row, RowBack, RowOver, Results>: Vie
8078
rowContent($element)
8179
.modifier(GridItemMod1(config: config,
8280
element: element,
83-
hovered: $hovered,
84-
selected: $selected))
81+
selected: $selected))
8582
.background(rowBackground(element))
8683
.overlay(rowOverlay(element))
8784
}

Sources/Grid/TablerGrid1C.swift

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ public struct TablerGrid1C<Element, Header, Row, RowBack, RowOver, Results>: Vie
3030
{
3131
public typealias Config = TablerGridConfig<Element>
3232
public typealias Context = TablerContext<Element>
33-
public typealias Hovered = Element.ID?
3433
public typealias HeaderContent = (Binding<Context>) -> Header
3534
public typealias ProjectedValue = ObservedObject<Element>.Wrapper
3635
public typealias RowContent = (ProjectedValue) -> Row
@@ -68,7 +67,6 @@ public struct TablerGrid1C<Element, Header, Row, RowBack, RowOver, Results>: Vie
6867

6968
// MARK: Locals
7069

71-
@State private var hovered: Hovered = nil
7270
@State private var context: Context
7371

7472
// MARK: Views
@@ -81,8 +79,7 @@ public struct TablerGrid1C<Element, Header, Row, RowBack, RowOver, Results>: Vie
8179
rowContent(obsElem)
8280
.modifier(GridItemMod1(config: config,
8381
element: rawElem,
84-
hovered: $hovered,
85-
selected: $selected))
82+
selected: $selected))
8683
.background(rowBackground(rawElem))
8784
.overlay(rowOverlay(rawElem))
8885
}

Sources/Grid/TablerGridB.swift

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ public struct TablerGridB<Element, Header, Row, RowBack, RowOver, Results>: View
3131
{
3232
public typealias Config = TablerGridConfig<Element>
3333
public typealias Context = TablerContext<Element>
34-
public typealias Hovered = Element.ID?
3534
public typealias HeaderContent = (Binding<Context>) -> Header
3635
public typealias RowContent = (Binding<Element>) -> Row
3736
public typealias RowBackground = (Element) -> RowBack
@@ -64,7 +63,6 @@ public struct TablerGridB<Element, Header, Row, RowBack, RowOver, Results>: View
6463

6564
// MARK: Locals
6665

67-
@State private var hovered: Hovered = nil
6866
@State private var context: Context
6967

7068
// MARK: Views
@@ -75,8 +73,7 @@ public struct TablerGridB<Element, Header, Row, RowBack, RowOver, Results>: View
7573
ForEach($results) { $element in
7674
rowContent($element)
7775
.modifier(GridItemMod(config: config,
78-
element: element,
79-
hovered: $hovered))
76+
element: element))
8077
.background(rowBackground(element))
8178
.overlay(rowOverlay(element))
8279
}

Sources/Grid/TablerGridC.swift

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ public struct TablerGridC<Element, Header, Row, RowBack, RowOver, Results>: View
3030
{
3131
public typealias Config = TablerGridConfig<Element>
3232
public typealias Context = TablerContext<Element>
33-
public typealias Hovered = Element.ID?
3433
public typealias HeaderContent = (Binding<Context>) -> Header
3534
public typealias ProjectedValue = ObservedObject<Element>.Wrapper
3635
public typealias RowContent = (ProjectedValue) -> Row
@@ -64,7 +63,6 @@ public struct TablerGridC<Element, Header, Row, RowBack, RowOver, Results>: View
6463

6564
// MARK: Locals
6665

67-
@State private var hovered: Hovered = nil
6866
@State private var context: Context
6967

7068
// MARK: Views
@@ -76,8 +74,7 @@ public struct TablerGridC<Element, Header, Row, RowBack, RowOver, Results>: View
7674
ObservableHolder(element: rawElem) { obsElem in
7775
rowContent(obsElem)
7876
.modifier(GridItemMod(config: config,
79-
element: rawElem,
80-
hovered: $hovered))
77+
element: rawElem))
8178
.background(rowBackground(rawElem))
8279
.overlay(rowOverlay(rawElem))
8380
}

0 commit comments

Comments
 (0)