fixed gridstack

This commit is contained in:
Turtuvshin
2024-01-23 18:56:45 +08:00
parent fc1e610a54
commit 3a72cacc5c
17 changed files with 461 additions and 272 deletions

View File

@@ -1,10 +1,8 @@
<div class="fleetbase-dashboard-grid">
<div class="grid grid-cols-1 md:grid-cols-12 gap-4">
<Button @type="default" @icon="add" @helpText={{"Add new widgets to Dashbord"}} @onClick={{this.openWidget}} />
{{#each this.dashboards as |dashboard index|}}
<Dashboard::Create @index={{index}} @dashboard={{dashboard}} />
{{/each}}
<Dashboard::WidgetPanel @isOpen={{this.isOpenWidget}} />
<div class="flex flex-row">
<Button @type="default" @icon="add" @helpText={{"Add new widgets to Dashbord"}} @onClick={{this.openWidgetSelector}} />
</div>
</div>
<Dashboard::Create @dashboard={{this.currentDashboard}} />
</div>
<Dashboard::WidgetPanel @isOpen={{this.isSelectingWidgets}} @onLoad={{this.setWidgetSelectorPanelContext}} @dashboard={{this.currentDashboard}} />

View File

@@ -2,55 +2,35 @@ import Component from '@glimmer/component';
import loadExtensions from '@fleetbase/ember-core/utils/load-extensions';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';
import { isArray } from '@ember/array';
import { tracked } from '@glimmer/tracking';
import { task } from 'ember-concurrency-decorators';
export default class DashboardComponent extends Component {
@service fetch;
@tracked extensions;
@service store;
@tracked dashboards = [];
@tracked isLoading;
@tracked isOpenWidget = false;
@tracked currentDashboard;
@tracked isSelectingWidgets = false;
constructor() {
super(...arguments);
this.loadExtensions();
this.loadDashboards.perform();
}
@action async loadExtensions() {
this.extensions = await loadExtensions();
console.log('Extensions:', this.extensions);
this.loadDashboardBuilds.perform();
@task *loadDashboards() {
this.dashboards = yield this.store.findAll('dashboard');
this.currentDashboard = this.dashboards[0];
}
@task *loadDashboard(extension) {
this.isLoading = extension.extension;
let dashboardBuild;
try {
dashboardBuild = yield this.fetch.get(extension.fleetbase.dashboard, {}, { namespace: '' });
} catch {
return;
}
if (isArray(dashboardBuild)) {
this.dashboards = [...this.dashboards, ...dashboardBuild.map((build) => ({ ...build, extension }))];
}
@action selectDashboard(dashboard) {
this.currentDashboard = dashboard;
}
@task({ enqueue: true, maxConcurrency: 1 }) *loadDashboardBuilds() {
const extensionsWithDashboards = this.extensions.filter((extension) => typeof extension.fleetbase?.dashboard === 'string');
console.log('Extensions with Dashboards:', extensionsWithDashboards);
for (let i = 0; i < extensionsWithDashboards.length; i++) {
const extension = extensionsWithDashboards[i];
yield this.loadDashboard.perform(extension);
}
@action setWidgetSelectorPanelContext(widgetSelectorContext) {
this.widgetSelectorContext = widgetSelectorContext;
console.log('widgetSelectorContext', widgetSelectorContext);
}
@action openWidget() {
this.isOpenWidget = !this.isOpenWidget;
console.log(this.isOpenWidget);
@action openWidgetSelector() {
this.isSelectingWidgets = true;
}
}

View File

@@ -1,42 +1,13 @@
<div class="col-span-{{or @dashboard.size 12}}">
<div class="dashboard-title flex flex-col lg:flex-row lg:items-center">
<div class="flex flex-row items-center mb-2 lg:mb-0">
{{#if this.isLoading}}
<Spinner class="mr-2i" />
{{/if}}
<h2 class="text-sm font-bold dark:text-gray-100 text-black">{{@dashboard.title}}</h2>
</div>
<div>
<Dashboard::QueryParams @params={{@dashboard.queryParams}} @onChange={{this.onQueryParamsChanged}} />
</div>
</div>
<div class="grid grid-cols-2 lg:grid-cols-12 gap-4">
{{#each this.dashboard.widgets as |widget|}}
{{component (concat "dashboard/" widget.component) options=widget.options}}
{{/each}}
</div>
<GridStack>
<GridStackItem @options={{hash x=0 y=0 w=6 h=2}}>
Widget #1
</GridStackItem>
</GridStack>
<GridStack @options={{hash animate=true column=12 maxRow=10}}>
<div class="fleetbase-dashboard-grid" ...attributes>
<GridStack
@options={{hash float=true animate=true acceptWidgets=true}}
@onChange={{this.onChangeGrid}}
@dragInOptions={{hash selector="test" options=(hash appendTo="body" helper="clone")}}
>
{{#each this.dashboard.widgets as |widget index|}}
<GridStackItem @options={{hash w=3 h=3 autoPosition=true}} class="min-w-16">
{{component (concat "dashboard/" widget.component) options=widget.options}}
<GridStackItem @options={{widget.gridOptions}}>
{{component widget.component options=widget.options}}
</GridStackItem>
{{/each}}
</GridStack>
<WidgetGridStack::WidgetGridStack @options={{hash float=false animate=false}} class="w-full grid gap-4 grid-cols-3 grid-rows-3">
{{#each this.dashboard.widgets as |widget index|}}
<WidgetGridStack::WidgetGridStackItem @options={{hash w=2 h=2}}>
{{component (concat "dashboard/" widget.component) options=widget.options}}
</WidgetGridStack::WidgetGridStackItem>
{{/each}}
</WidgetGridStack::WidgetGridStack>
</div>

View File

@@ -2,40 +2,111 @@ import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';
import { isArray } from '@ember/array';
import { isArray, A } from '@ember/array';
import { task } from 'ember-concurrency-decorators';
import FleetbaseBlogComponent from '../fleetbase-blog';
import GithubCardComponent from '../github-card';
import DashboardCountComponent from './count';
export default class DashboardCreateComponent extends Component {
@service fetch;
@tracked isLoading = false;
@tracked dashboard;
constructor() {
constructor(owner, { dashboard }) {
super(...arguments);
this.dashboard = this.args.dashboard;
this.dashboard = {
title: 'Fleetops stats',
widgets: [
{
name: 'Blog',
component: FleetbaseBlogComponent,
gridOptions: { w: 8, h: 3, minW: 2 },
options: {},
},
{
name: 'Github',
component: GithubCardComponent,
gridOptions: { w: 4, h: 3, minW: 2 },
options: {},
},
{
name: 'Blog',
component: DashboardCountComponent,
gridOptions: { w: 2, h: 2, minW: 2 },
options: {
format: 'money',
value: '150',
currency: '$',
},
},
{
name: 'Blog',
component: DashboardCountComponent,
gridOptions: { w: 2, h: 2, minW: 2 },
options: {
format: 'money',
value: '150',
currency: '$',
},
},
{
name: 'Blog',
component: DashboardCountComponent,
gridOptions: { w: 2, h: 2, minW: 2 },
options: {
format: 'money',
value: '150',
currency: '$',
},
},
{
name: 'Blog',
component: DashboardCountComponent,
gridOptions: { w: 2, h: 2, minW: 2 },
options: {
format: 'money',
value: '150',
currency: '$',
},
},
{
name: 'Blog',
component: DashboardCountComponent,
gridOptions: { w: 2, h: 2, minW: 2 },
options: {
format: 'money',
value: '150',
currency: '$',
},
},
{
name: 'Blog',
component: DashboardCountComponent,
gridOptions: { w: 2, h: 2, minW: 2 },
options: {
format: 'money',
value: '150',
currency: '$',
},
},
],
};
}
@action
toggleFloat() {
this.shouldFloat = !this.shouldFloat;
}
@action onQueryParamsChanged(changedParams) {
this.reloadDashboard.perform(changedParams);
}
@task *reloadDashboard(params) {
const { extension } = this.args.dashboard;
const index = this.args.index;
let dashboards = [];
@action onChangeGrid(event) {
console.log('Grid Stack event: ', event);
}
this.isLoading = true;
try {
dashboards = yield this.fetch.get(extension.fleetbase.dashboard, params, { namespace: '' });
} catch {
return;
}
this.isLoading = false;
if (isArray(dashboards)) {
this.dashboard = dashboards.objectAt(index);
}
@action onDragToDashboard(event) {
console.log('Grid Stack drag event: ', event);
}
}

View File

@@ -7,12 +7,37 @@
</Overlay::Header>
<Overlay::Body @wrapperClass="new-service-rate-overlay-body px-4 space-y-4 pt-4" @increaseInnerBodyHeightBy={{1000}}>
<GridStack @options={{hash float=true animate=true}} @onChange={{this.onChangeGrid}} @onSetupDragIn={{this.onDragToDashboard}} as |grid|>
{{#each this.availableWidgets as |widget|}}
<GridStackItem @options={{hash w=4 h=1}}>
<div
class="flex flex-col items-center justify-center rounded-lg border border-gray-200 bg-gray-50 dark:border-gray-700 dark:bg-gray-800 shadow-sm mr-1"
{{on "click" (fn this.addWidgetToDashboard widget)}}
>
<p class="text-lg font-bold dark:text-gray-100 text-black p-4">
{{widget.name}}
widget
</p>
</div>
</GridStackItem>
{{/each}}
</GridStack>
<div class="grid grid-cols-3 gap-1 text-xs dark:text-gray-100">
<div class="grid grid-cols-2 lg:grid-cols-12 gap-4">
{{#each this.widgets as |widget|}}
{{component widget.options.component}}
{{/each}}
</div>
{{#each this.availableWidgets as |widget|}}
<GridStackItem @options={{widget.gridOptions}}>
<div
class="flex flex-col items-center justify-center rounded-lg border border-gray-200 bg-gray-50 dark:border-gray-700 dark:bg-gray-800 shadow-sm mr-1"
{{on "click" (fn this.addWidgetToDashboard widget)}}
>
<p class="text-lg font-bold dark:text-gray-100 text-black p-4">
{{widget.name}}
widget
</p>
</div>
</GridStackItem>
{{/each}}
</div>
</Overlay::Body>
</Overlay>

View File

@@ -1,28 +1,20 @@
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
export default class DashboardWidgetPanelComponent extends Component {
@tracked isLoading = false;
@tracked isOpen = false;
@service universe;
@tracked availableWidgets = [];
/**
* Constructs the component and applies initial state.
*/
constructor() {
constructor(owner, { dashboard }) {
super(...arguments);
console.log(this.args);
}
didReceiveArguments() {
if (this.args.isOpen !== this.isOpen) {
// Handle the change in isOpen here
this.isOpen = this.args.isOpen;
console.log('Parent isOpen changed:', this.isOpen);
}
this.availableWidgets = this.universe.getWidgets();
this.dashboard = dashboard;
}
/**
@@ -33,19 +25,15 @@ export default class DashboardWidgetPanelComponent extends Component {
*/
@action setOverlayContext(overlayContext) {
this.context = overlayContext;
console.log('Context: ', this.context, arguments);
if (typeof this.args.onLoad === 'function') {
this.args.onLoad(...arguments);
}
}
/**
* Saves the widget changes.
*
* @action
* @returns {Promise<any>}
*/
@action save() {}
@action addWidgetToDashboard(widget) {
this.dashboard.addWidget(widget);
}
/**
* Handles cancel button press.
@@ -54,11 +42,17 @@ export default class DashboardWidgetPanelComponent extends Component {
* @returns {any}
*/
@action onPressCancel() {
this.context.close();
// return contextComponentCallback(this, 'onPressCancel', this.widget);
}
get widgets() {
console.log(this.universe.getWidgets());
return this.universe.getWidgets();
@action onDragToDashboard(item) {
console.log('Event: ', item);
}
myClone(event) {
const el = event.target.cloneNode(true);
el.setAttribute('gs-id', 'foo'); // TEST why clone element is not used directly on drop #2231
return el;
}
}

View File

@@ -1,13 +1,62 @@
import DashboardCountComponent from '../components/dashboard/count';
import FleetbaseBlogComponent from '../components/fleetbase-blog';
import GithubCardComponent from '../components/github-card';
export function initialize(application) {
const universe = application.lookup('service:universe');
universe.registerWidget({
name: 'Fleetbase Blog',
position: {},
options: { component: FleetbaseBlogComponent },
});
const fleetOpsWidgets = [
{
name: 'Open Issues',
component: DashboardCountComponent,
gridOptions: { w: 2, h: 2 },
options: {},
},
];
const storefrontWidgets = [
{
name: 'Total Products',
component: DashboardCountComponent,
gridOptions: { w: 2, h: 2 },
options: {},
},
];
const iamWidgets = [
{
name: 'User count',
component: DashboardCountComponent,
gridOptions: { w: 2, h: 2 },
options: {},
},
{
name: 'Group count',
component: DashboardCountComponent,
gridOptions: { w: 2, h: 2 },
options: {},
},
];
const availableWidgets = [
{
name: 'Fleetbase Blog',
component: FleetbaseBlogComponent,
gridOptions: { w: 8, h: 3 },
options: {},
},
{
name: 'Github Card',
component: GithubCardComponent,
gridOptions: { w: 4, h: 3 },
options: {},
},
...fleetOpsWidgets,
...storefrontWidgets,
...iamWidgets,
];
universe.registerWidgets(availableWidgets);
}
export default {

View File

@@ -0,0 +1,3 @@
import Model from '@ember-data/model';
export default class DashboardWidgetModel extends Model {}

View File

@@ -0,0 +1,3 @@
import Model from '@ember-data/model';
export default class DashboardModel extends Model {}

View File

@@ -1,10 +1,6 @@
{{page-title "Dashboard"}}
<Layout::Section::Body class="overflow-y-scroll h-full pt-6">
<div class="console-home-container mx-auto h-screen space-y-4" {{increase-height-by 300}}>
<div class="grid grid-cols-1 md:grid-cols-12 gap-4">
<GithubCard class="lg:col-span-4" />
<FleetbaseBlog class="lg:col-span-8" />
</div>
<Dashboard @sidebar={{this.sidebarContext}} />
</div>
</Layout::Section::Body>

View File

@@ -21,6 +21,7 @@
"lint:hbs:fix": "ember-template-lint . --fix",
"lint:js": "eslint . --cache",
"lint:js:fix": "eslint . --fix",
"postinstall": "patch-package",
"start": "pnpm run prebuild && ember serve",
"test": "concurrently \"npm:lint\" \"npm:test:*\" --names \"lint,test:\"",
"test:ember": "ember test"
@@ -29,7 +30,6 @@
"@ember/legacy-built-in-components": "^0.4.1",
"@fleetbase/ember-core": "link:../packages/ember-core",
"@fleetbase/ember-ui": "link:../packages/ember-ui",
"@fleetbase/fleetops-engine": "^0.3.8",
"@fleetbase/leaflet-routing-machine": "^3.2.16",
"@fortawesome/ember-fontawesome": "^0.4.1",
"ember-changeset": "^4.1.2",
@@ -45,7 +45,8 @@
"ember-radio-button": "3.0.0-beta.1",
"ember-tag-input": "^3.1.0",
"fleetbase-extensions-indexer": "^0.0.4",
"gridstack": "^10.0.1",
"gridstack": "^7.2.2",
"patch-package": "^8.0.0",
"postcss-at-rules-variables": "^0.3.0",
"postcss-custom-properties": "^12.1.9",
"postcss-nth-list": "^1.0.2"

View File

@@ -0,0 +1,34 @@
diff --git a/node_modules/ember-gridstack/addon/components/grid-stack.js b/node_modules/ember-gridstack/addon/components/grid-stack.js
index fa51392..ed247af 100644
--- a/node_modules/ember-gridstack/addon/components/grid-stack.js
+++ b/node_modules/ember-gridstack/addon/components/grid-stack.js
@@ -40,7 +40,7 @@ export const GRID_STACK_EVENTS = [
'removed',
'resizestart',
'resize',
- 'resizestop',
+ 'resizestop'
];
export default class GridStackComponent extends Component {
@@ -52,6 +52,7 @@ export default class GridStackComponent extends Component {
constructor() {
super(...arguments);
this.gridStackRegistry.registerGrid(this.guid, this);
+
}
get options() {
@@ -93,6 +94,12 @@ export default class GridStackComponent extends Component {
_createGridStack() {
this.gridStack = GridStack.init({ ...this.options }, this.elm);
+ if(this.dragInOptions){
+ const { selector, options } = this.dragInOptions
+ console.log("Setup Drag in: ", this.dragInOptions)
+ this.gridStack?.setupDragIn(selector, options);
+ }
+
GRID_STACK_EVENTS.forEach((eventName) => {
const action = this.args[`on${capitalize(eventName)}`];

292
console/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,14 @@
import { module, test } from 'qunit';
import { setupTest } from '@fleetbase/console/tests/helpers';
module('Unit | Model | dashboard', function (hooks) {
setupTest(hooks);
// Replace this with your real tests.
test('it exists', function (assert) {
let store = this.owner.lookup('service:store');
let model = store.createRecord('dashboard', {});
assert.ok(model);
});
});

View File

@@ -0,0 +1,14 @@
import { module, test } from 'qunit';
import { setupTest } from '@fleetbase/console/tests/helpers';
module('Unit | Model | dashboard widget', function (hooks) {
setupTest(hooks);
// Replace this with your real tests.
test('it exists', function (assert) {
let store = this.owner.lookup('service:store');
let model = store.createRecord('dashboard-widget', {});
assert.ok(model);
});
});

4
yarn.lock Normal file
View File

@@ -0,0 +1,4 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1