diff --git a/docs/configuration.md b/docs/configuration.md index 242e9ea..d5856ca 100644 --- a/docs/configuration.md +++ b/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: + +
+View config + +```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 +``` +
+ +
+ +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). diff --git a/docs/images/split-column-widget-preview.png b/docs/images/split-column-widget-preview.png new file mode 100644 index 0000000..f1931f8 Binary files /dev/null and b/docs/images/split-column-widget-preview.png differ diff --git a/internal/assets/static/js/main.js b/internal/assets/static/js/main.js index ffa7eb7..ed8419a 100644 --- a/internal/assets/static/js/main.js +++ b/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 { diff --git a/internal/assets/templates.go b/internal/assets/templates.go index 85abb69..0533b75 100644 --- a/internal/assets/templates.go +++ b/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{ diff --git a/internal/assets/templates/split-column.html b/internal/assets/templates/split-column.html new file mode 100644 index 0000000..d1d3386 --- /dev/null +++ b/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" }} +
+{{ range .Widgets }} + {{ .Render }} +{{ end }} +
+{{ end }} diff --git a/internal/widget/container.go b/internal/widget/container.go index 9113ea3..db07bb7 100644 --- a/internal/widget/container.go +++ b/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 diff --git a/internal/widget/group.go b/internal/widget/group.go index c4bd101..2725aba 100644 --- a/internal/widget/group.go +++ b/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 { diff --git a/internal/widget/split-column.go b/internal/widget/split-column.go new file mode 100644 index 0000000..88b9edb --- /dev/null +++ b/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) +} diff --git a/internal/widget/widget.go b/internal/widget/widget.go index c452427..0837f43 100644 --- a/internal/widget/widget.go +++ b/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) }