Skip to content

Commit

Permalink
Add grid functionality (#67)
Browse files Browse the repository at this point in the history
  • Loading branch information
milewski authored Oct 24, 2023
1 parent 425fa1d commit a371d10
Show file tree
Hide file tree
Showing 10 changed files with 206 additions and 9 deletions.
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ class Main extends Dashboard
}
```

#### Static

By default, each widget is draggable, and the user is able to rearrange it to their liking.
This behavior can be disabled by calling `$view->static()`.

## Widgets

The widgets are responsible for displaying your data on your views; they are essentially standard Nova cards.
Expand Down Expand Up @@ -93,9 +98,19 @@ class MyCustomWidget extends ValueWidget
}
```

All widgets have common methods to configure their size and position.
The value is not in pixels but in grid units, ranging from `1` to `12` (corresponding to 12 columns).

```php
$widget->layout(width: 2, height: 1, x: 0, y: 1);
$widget->minWidth(2);
$widget->minHeight(1);
```

### List of current available widgets:

- Value Widget: [https://github.com/dcasia/value-widget](https://github.com/dcasia/value-widget)
- Table Widget: [https://github.com/dcasia/table-widget](https://github.com/dcasia/table-widget)
- [Add your widget here.](https://github.com/dcasia/nova-dashboard/edit/main/README.md)

## Filters
Expand Down
2 changes: 1 addition & 1 deletion dist/css/card.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/js/card.js

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions dist/js/card.js.LICENSE.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
/*!
* GridStack 9.4.0
* https://gridstackjs.com/
*
* Copyright (c) 2021-2022 Alain Dumesny
* see root license https://github.com/gridstack/gridstack.js/tree/master/LICENSE
*/

/*!
* vuex v4.1.0
* (c) 2022 Evan You
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
},
"devDependencies": {
"@vue/compiler-sfc": "^3.3.4",
"gridstack": "^9.4.0",
"laravel-mix": "^6.0.49",
"mix-tailwindcss": "^1.3.0",
"sass": "^1.68.0",
Expand Down
104 changes: 102 additions & 2 deletions resources/js/components/Card.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,13 @@
:via-relationship="viaRelationship"
@toggle="onViewChange"/>

<Cards v-if="activeView && activeView.widgets.length" :cards="activeView.widgets"/>
<div class="flex justify-center w-[calc(100%+24px)] -ml-[12px]">

<div ref="gridStack" class="grid-stack h-full w-full">
<Widget v-for="widget in activeView.widgets" :widget="widget" :key="widget.key"/>
</div>

</div>

</div>

Expand All @@ -25,9 +31,11 @@
import Filter from './Filter.vue'
import resourceStore from '@/store/resources'
import { GridStack } from 'gridstack'
import Widget from './Widget.vue'
export default {
components: { Filter },
components: { Filter, Widget },
props: [
'card',
'lens',
Expand All @@ -42,10 +50,17 @@
const view = this.card.views.find(view => view.key === key)
return {
grid: null,
activeView: view ?? this.card.views[ 0 ],
}
},
mounted() {
this.createGrid()
},
unmounted() {
this.destroyGrid()
},
created() {
for (const view of this.card.views) {
Expand All @@ -60,7 +75,92 @@
}
},
watch: {
activeView() {
this.createGrid()
},
},
computed: {
cacheKey() {
return `${ this.activeView.key }-widgets`
},
},
methods: {
destroyGrid() {
if (this.grid) {
this.grid.offAll()
this.grid.destroy(false)
this.$refs.gridStack?.removeAttribute('gs-static')
}
},
createGrid() {
this.destroyGrid()
const margin = 12
this.grid = GridStack.init({
staticGrid: this.activeView.static,
cellHeight: 160 + margin * 2,
margin: margin,
animate: false,
auto: false,
})
this.$nextTick(() => {
const savedWidgets = this.loadGrid()
for (const widget of this.activeView.widgets) {
const savedWidget = savedWidgets.find(item => item.id === widget.key)
const layout = this.activeView.static === true
? widget
: savedWidget ?? widget
this.grid.makeWidget(`#${ widget.key }`, {
autoPosition: false,
id: widget.key,
minW: widget.minWidth,
minH: widget.minHeight,
x: layout.x,
y: layout.y,
w: layout.width,
h: layout.height,
})
}
this.grid.setAnimation(true)
this.grid.on('drag', () => this.saveGrid())
this.grid.on('resize', () => this.saveGrid())
})
},
loadGrid() {
try {
return JSON.parse(localStorage.getItem(this.cacheKey)) ?? []
} catch {
return []
}
},
saveGrid() {
const nodes = this.grid.engine.nodes.map(node => ({
id: node.id,
width: node.w,
height: node.h,
x: node.x,
y: node.y,
}))
localStorage.setItem(this.cacheKey, JSON.stringify(nodes))
},
onViewChange(view) {
this.activeView = view
},
Expand Down
31 changes: 31 additions & 0 deletions resources/js/components/Widget.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<template>

<div class="grid-stack-item" :id="widget.key">

<div class="grid-stack-item-content">

<component
class="h-full w-full"
:key="`${ widget.component }.${ widget.key }`"
:is="widget.component"
:card="{ ...widget, uriKey: widget.key }"
/>

</div>

</div>

</template>

<script>
export default {
props: [ 'widget' ],
}
</script>

<style>
@import "gridstack/dist/gridstack.min.css";
</style>

5 changes: 5 additions & 0 deletions src/Card/View.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ public function icon(string $icon): self
return $this->withMeta([ 'icon' => $icon ]);
}

public function static(): self
{
return $this->withMeta([ 'static' => true ]);
}

public function key(): string
{
return md5($this->name);
Expand Down
42 changes: 37 additions & 5 deletions src/Card/Widget.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,16 @@

use DigitalCreative\NovaDashboard\Filters;
use DigitalCreative\NovaDashboard\Traits\GuessCaller;
use Illuminate\Support\Str;
use Laravel\Nova\Card;
use Laravel\Nova\Element;
use Laravel\Nova\Http\Requests\NovaRequest;

abstract class Widget extends Card
abstract class Widget extends Element
{
use GuessCaller;

public function key(): string
{
return static::class;
return md5(static::class);
}

abstract public function value(Filters $filters);
Expand All @@ -34,6 +33,36 @@ public function resolveValue(NovaRequest $request, ?View $view = null): mixed
);
}

public function layout(int $width, int $height, int $x, int $y): self
{
return $this->width($width)->height($height)->position($x, $y);
}

public function position(int $x, int $y): self
{
return $this->withMeta([ 'x' => $x, 'y' => $y ]);
}

public function width(int $width): self
{
return $this->withMeta([ 'width' => $width ]);
}

public function height(int $height): self
{
return $this->withMeta([ 'height' => $height ]);
}

public function minWidth(int $width): self
{
return $this->withMeta([ 'minWidth' => $width ]);
}

public function minHeight(int $height): self
{
return $this->withMeta([ 'minHeight' => $height ]);
}

public function jsonSerialize(): array
{
$request = resolve(NovaRequest::class);
Expand All @@ -42,8 +71,11 @@ public function jsonSerialize(): array

return array_merge([
'key' => $this->key(),
'uriKey' => Str::random(),
'value' => $this->resolveValue($request),
'width' => 2,
'height' => 1,
'x' => 0,
'y' => 0,
], parent::jsonSerialize());
}
}
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2946,6 +2946,11 @@ graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2,
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==

gridstack@^9.4.0:
version "9.4.0"
resolved "https://registry.yarnpkg.com/gridstack/-/gridstack-9.4.0.tgz#806b5f8dc2d52ac6aade5d5fc799979a167f6977"
integrity sha512-4dcXGT5IzWfLxJIj4oGZPohgYfHrbdNHenRwwApzialkG2bVd9J9NmcdZsRFX6Q4cANqR0M56yF2Km2FAQkVCw==

growly@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
Expand Down

0 comments on commit a371d10

Please sign in to comment.