mirror of
https://github.com/glanceapp/glance.git
synced 2025-06-22 02:41:23 +02:00
Add split-column widget
This commit is contained in:
parent
13700fe2b2
commit
e5bb102ab1
@ -15,6 +15,7 @@
|
|||||||
- [Reddit](#reddit)
|
- [Reddit](#reddit)
|
||||||
- [Search](#search-widget)
|
- [Search](#search-widget)
|
||||||
- [Group](#group)
|
- [Group](#group)
|
||||||
|
- [Split Column](#split-column)
|
||||||
- [Extension](#extension)
|
- [Extension](#extension)
|
||||||
- [Weather](#weather)
|
- [Weather](#weather)
|
||||||
- [Monitor](#monitor)
|
- [Monitor](#monitor)
|
||||||
@ -890,7 +891,7 @@ url: https://www.amazon.com/s?k={QUERY}
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Group
|
### 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:
|
Example:
|
||||||
|
|
||||||
@ -933,6 +934,63 @@ Example:
|
|||||||
<<: *shared-properties
|
<<: *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:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
### Extension
|
### 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).
|
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
Normal file
BIN
docs/images/split-column-widget-preview.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 328 KiB |
@ -1,4 +1,5 @@
|
|||||||
import { setupPopovers } from './popover.js';
|
import { setupPopovers } from './popover.js';
|
||||||
|
import { setupMasonries } from './masonry.js';
|
||||||
import { throttledDebounce, isElementVisible } from './utils.js';
|
import { throttledDebounce, isElementVisible } from './utils.js';
|
||||||
|
|
||||||
async function fetchPageContent(pageData) {
|
async function fetchPageContent(pageData) {
|
||||||
@ -581,6 +582,7 @@ async function setupPage() {
|
|||||||
setupCollapsibleLists();
|
setupCollapsibleLists();
|
||||||
setupCollapsibleGrids();
|
setupCollapsibleGrids();
|
||||||
setupGroups();
|
setupGroups();
|
||||||
|
setupMasonries();
|
||||||
setupDynamicRelativeTime();
|
setupDynamicRelativeTime();
|
||||||
setupLazyImages();
|
setupLazyImages();
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -39,6 +39,7 @@ var (
|
|||||||
ExtensionTemplate = compileTemplate("extension.html", "widget-base.html")
|
ExtensionTemplate = compileTemplate("extension.html", "widget-base.html")
|
||||||
GroupTemplate = compileTemplate("group.html", "widget-base.html")
|
GroupTemplate = compileTemplate("group.html", "widget-base.html")
|
||||||
DNSStatsTemplate = compileTemplate("dns-stats.html", "widget-base.html")
|
DNSStatsTemplate = compileTemplate("dns-stats.html", "widget-base.html")
|
||||||
|
SplitColumnTemplate = compileTemplate("split-column.html", "widget-base.html")
|
||||||
)
|
)
|
||||||
|
|
||||||
var globalTemplateFunctions = template.FuncMap{
|
var globalTemplateFunctions = template.FuncMap{
|
||||||
|
11
internal/assets/templates/split-column.html
Normal file
11
internal/assets/templates/split-column.html
Normal file
@ -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 }}
|
@ -6,11 +6,11 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type containerWidget struct {
|
type containerWidgetBase struct {
|
||||||
Widgets Widgets `yaml:"widgets"`
|
Widgets Widgets `yaml:"widgets"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (widget *containerWidget) Update(ctx context.Context) {
|
func (widget *containerWidgetBase) Update(ctx context.Context) {
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
@ -31,13 +31,13 @@ func (widget *containerWidget) Update(ctx context.Context) {
|
|||||||
wg.Wait()
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (widget *containerWidget) SetProviders(providers *Providers) {
|
func (widget *containerWidgetBase) SetProviders(providers *Providers) {
|
||||||
for i := range widget.Widgets {
|
for i := range widget.Widgets {
|
||||||
widget.Widgets[i].SetProviders(providers)
|
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 {
|
for i := range widget.Widgets {
|
||||||
if widget.Widgets[i].RequiresUpdate(now) {
|
if widget.Widgets[i].RequiresUpdate(now) {
|
||||||
return true
|
return true
|
||||||
|
@ -10,8 +10,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Group struct {
|
type Group struct {
|
||||||
widgetBase `yaml:",inline"`
|
widgetBase `yaml:",inline"`
|
||||||
containerWidget `yaml:",inline"`
|
containerWidgetBase `yaml:",inline"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (widget *Group) Initialize() error {
|
func (widget *Group) Initialize() error {
|
||||||
@ -22,7 +22,9 @@ func (widget *Group) Initialize() error {
|
|||||||
widget.Widgets[i].SetHideHeader(true)
|
widget.Widgets[i].SetHideHeader(true)
|
||||||
|
|
||||||
if widget.Widgets[i].GetType() == "group" {
|
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 {
|
if err := widget.Widgets[i].Initialize(); err != nil {
|
||||||
@ -34,15 +36,15 @@ func (widget *Group) Initialize() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (widget *Group) Update(ctx context.Context) {
|
func (widget *Group) Update(ctx context.Context) {
|
||||||
widget.containerWidget.Update(ctx)
|
widget.containerWidgetBase.Update(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (widget *Group) SetProviders(providers *Providers) {
|
func (widget *Group) SetProviders(providers *Providers) {
|
||||||
widget.containerWidget.SetProviders(providers)
|
widget.containerWidgetBase.SetProviders(providers)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (widget *Group) RequiresUpdate(now *time.Time) bool {
|
func (widget *Group) RequiresUpdate(now *time.Time) bool {
|
||||||
return widget.containerWidget.RequiresUpdate(now)
|
return widget.containerWidgetBase.RequiresUpdate(now)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (widget *Group) Render() template.HTML {
|
func (widget *Group) Render() template.HTML {
|
||||||
|
42
internal/widget/split-column.go
Normal file
42
internal/widget/split-column.go
Normal file
@ -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)
|
||||||
|
}
|
@ -67,6 +67,8 @@ func New(widgetType string) (Widget, error) {
|
|||||||
widget = &Group{}
|
widget = &Group{}
|
||||||
case "dns-stats":
|
case "dns-stats":
|
||||||
widget = &DNSStats{}
|
widget = &DNSStats{}
|
||||||
|
case "split-column":
|
||||||
|
widget = &SplitColumn{}
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unknown widget type: %s", widgetType)
|
return nil, fmt.Errorf("unknown widget type: %s", widgetType)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user