Browse Source

Add split-column widget

Svilen Markov 8 months ago
parent
commit
e5bb102ab1

+ 59 - 1
docs/configuration.md

@@ -15,6 +15,7 @@
   - [Reddit](#reddit)
   - [Search](#search-widget)
   - [Group](#group)
+  - [Split Column](#split-column)
   - [Extension](#extension)
   - [Weather](#weather)
   - [Monitor](#monitor)
@@ -890,7 +891,7 @@ url: https://www.amazon.com/s?k={QUERY}
 ```
 
 ### Group
-Group multiple widgets into one using tabs. Widgets are defined using a `widgets` property exactly as you would on a page column. The only limitation is that you cannot place a group widget within a group widget.
+Group multiple widgets into one using tabs. Widgets are defined using a `widgets` property exactly as you would on a page column. The only limitation is that you cannot place a group widget or a split column widget within a group widget.
 
 Example:
 
@@ -933,6 +934,63 @@ Example:
       <<: *shared-properties
 ```
 
+### Split Column
+Splits a full sized column in half, allowing you to place widgets side by side. This is converted to a single column on mobile devices or if not enough width is available. Widgets are defined using a `widgets` property exactly as you would on a page column.
+
+Example of a full page with an effective 4 column layout using two split column widgets inside of two full sized columns:
+
+<details>
+<summary>View config</summary>
+
+```yaml
+shared:
+  - &reddit-props
+    type: reddit
+    collapse-after: 4
+    show-thumbnails: true
+
+pages:
+  - name: Split Column Demo
+    width: wide
+    columns:
+      - size: full
+        widgets:
+          - type: split-column
+            widgets:
+              - subreddit: gaming
+                <<: *reddit-props
+              - subreddit: worldnews
+                <<: *reddit-props
+              - subreddit: lifeprotips
+                <<: *reddit-props
+                show-thumbnails: false
+              - subreddit: askreddit
+                <<: *reddit-props
+                show-thumbnails: false
+
+      - size: full
+        widgets:
+          - type: split-column
+            widgets:
+              - subreddit: todayilearned
+                <<: *reddit-props
+                collapse-after: 2
+              - subreddit: aww
+                <<: *reddit-props
+              - subreddit: science
+                <<: *reddit-props
+              - subreddit: showerthoughts
+                <<: *reddit-props
+                show-thumbnails: false
+```
+</details>
+
+<br>
+
+Preview:
+
+![](images/split-column-widget-preview.png)
+
 ### Extension
 Display a widget provided by an external source (3rd party). If you want to learn more about developing extensions, checkout the [extensions documentation](extensions.md) (WIP).
 

BIN
docs/images/split-column-widget-preview.png


+ 2 - 0
internal/assets/static/js/main.js

@@ -1,4 +1,5 @@
 import { setupPopovers } from './popover.js';
+import { setupMasonries } from './masonry.js';
 import { throttledDebounce, isElementVisible } from './utils.js';
 
 async function fetchPageContent(pageData) {
@@ -581,6 +582,7 @@ async function setupPage() {
         setupCollapsibleLists();
         setupCollapsibleGrids();
         setupGroups();
+        setupMasonries();
         setupDynamicRelativeTime();
         setupLazyImages();
     } finally {

+ 1 - 0
internal/assets/templates.go

@@ -39,6 +39,7 @@ var (
 	ExtensionTemplate             = compileTemplate("extension.html", "widget-base.html")
 	GroupTemplate                 = compileTemplate("group.html", "widget-base.html")
 	DNSStatsTemplate              = compileTemplate("dns-stats.html", "widget-base.html")
+	SplitColumnTemplate           = compileTemplate("split-column.html", "widget-base.html")
 )
 
 var globalTemplateFunctions = template.FuncMap{

+ 11 - 0
internal/assets/templates/split-column.html

@@ -0,0 +1,11 @@
+{{ template "widget-base.html" . }}
+
+{{ define "widget-content-classes" }}widget-content-frameless{{ end }}
+
+{{ define "widget-content" }}
+<div class="masonry" data-max-columns="2">
+{{ range .Widgets }}
+    {{ .Render }}
+{{ end }}
+</div>
+{{ end }}

+ 4 - 4
internal/widget/container.go

@@ -6,11 +6,11 @@ import (
 	"time"
 )
 
-type containerWidget struct {
+type containerWidgetBase struct {
 	Widgets Widgets `yaml:"widgets"`
 }
 
-func (widget *containerWidget) Update(ctx context.Context) {
+func (widget *containerWidgetBase) Update(ctx context.Context) {
 	var wg sync.WaitGroup
 	now := time.Now()
 
@@ -31,13 +31,13 @@ func (widget *containerWidget) Update(ctx context.Context) {
 	wg.Wait()
 }
 
-func (widget *containerWidget) SetProviders(providers *Providers) {
+func (widget *containerWidgetBase) SetProviders(providers *Providers) {
 	for i := range widget.Widgets {
 		widget.Widgets[i].SetProviders(providers)
 	}
 }
 
-func (widget *containerWidget) RequiresUpdate(now *time.Time) bool {
+func (widget *containerWidgetBase) RequiresUpdate(now *time.Time) bool {
 	for i := range widget.Widgets {
 		if widget.Widgets[i].RequiresUpdate(now) {
 			return true

+ 8 - 6
internal/widget/group.go

@@ -10,8 +10,8 @@ import (
 )
 
 type Group struct {
-	widgetBase      `yaml:",inline"`
-	containerWidget `yaml:",inline"`
+	widgetBase          `yaml:",inline"`
+	containerWidgetBase `yaml:",inline"`
 }
 
 func (widget *Group) Initialize() error {
@@ -22,7 +22,9 @@ func (widget *Group) Initialize() error {
 		widget.Widgets[i].SetHideHeader(true)
 
 		if widget.Widgets[i].GetType() == "group" {
-			return errors.New("nested groups are not allowed")
+			return errors.New("nested groups are not supported")
+		} else if widget.Widgets[i].GetType() == "split-column" {
+			return errors.New("split columns inside of groups are not supported")
 		}
 
 		if err := widget.Widgets[i].Initialize(); err != nil {
@@ -34,15 +36,15 @@ func (widget *Group) Initialize() error {
 }
 
 func (widget *Group) Update(ctx context.Context) {
-	widget.containerWidget.Update(ctx)
+	widget.containerWidgetBase.Update(ctx)
 }
 
 func (widget *Group) SetProviders(providers *Providers) {
-	widget.containerWidget.SetProviders(providers)
+	widget.containerWidgetBase.SetProviders(providers)
 }
 
 func (widget *Group) RequiresUpdate(now *time.Time) bool {
-	return widget.containerWidget.RequiresUpdate(now)
+	return widget.containerWidgetBase.RequiresUpdate(now)
 }
 
 func (widget *Group) Render() template.HTML {

+ 42 - 0
internal/widget/split-column.go

@@ -0,0 +1,42 @@
+package widget
+
+import (
+	"context"
+	"html/template"
+	"time"
+
+	"github.com/glanceapp/glance/internal/assets"
+)
+
+type SplitColumn struct {
+	widgetBase          `yaml:",inline"`
+	containerWidgetBase `yaml:",inline"`
+}
+
+func (widget *SplitColumn) Initialize() error {
+	widget.withError(nil).withTitle("Split Column").SetHideHeader(true)
+
+	for i := range widget.Widgets {
+		if err := widget.Widgets[i].Initialize(); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func (widget *SplitColumn) Update(ctx context.Context) {
+	widget.containerWidgetBase.Update(ctx)
+}
+
+func (widget *SplitColumn) SetProviders(providers *Providers) {
+	widget.containerWidgetBase.SetProviders(providers)
+}
+
+func (widget *SplitColumn) RequiresUpdate(now *time.Time) bool {
+	return widget.containerWidgetBase.RequiresUpdate(now)
+}
+
+func (widget *SplitColumn) Render() template.HTML {
+	return widget.render(widget, assets.SplitColumnTemplate)
+}

+ 2 - 0
internal/widget/widget.go

@@ -67,6 +67,8 @@ func New(widgetType string) (Widget, error) {
 		widget = &Group{}
 	case "dns-stats":
 		widget = &DNSStats{}
+	case "split-column":
+		widget = &SplitColumn{}
 	default:
 		return nil, fmt.Errorf("unknown widget type: %s", widgetType)
 	}