@lobb-js/lobb-ext-reports 0.14.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,7 +8,7 @@
8
8
  const componentProps: any = {
9
9
  title: "Generate the query with AI",
10
10
  description: "Describe to the AI the query you want to generate",
11
- class: "h-7 px-3 text-xs font-normal",
11
+ size: "sm",
12
12
  variant: "outline",
13
13
  // format: {
14
14
  // type: 'json_object'
@@ -9,16 +9,14 @@
9
9
  column?: number;
10
10
  cellHeight?: string;
11
11
  editable?: boolean;
12
- allowEdit?: boolean;
13
12
  onLayoutChange?: (layout: LayoutItem[]) => void;
14
13
  children: Snippet;
15
14
  }
16
15
 
17
16
  let {
18
17
  column = 12,
19
- cellHeight = "150px",
18
+ cellHeight = "50px",
20
19
  editable = $bindable(false),
21
- allowEdit = false,
22
20
  onLayoutChange,
23
21
  children,
24
22
  }: Props = $props();
@@ -31,18 +29,16 @@
31
29
  let containerWidth = $state(0);
32
30
  let saveTimeout: ReturnType<typeof setTimeout> | null = null;
33
31
 
34
- // Edit mode only ever turns on when the parent explicitly opts in.
35
- // Without allowEdit, container width is irrelevant the grid stays
36
- // disabled forever.
37
- let editMode = $derived(allowEdit && containerWidth >= 1200);
38
-
39
- // Keep editable in sync so parent can read it
40
- $effect(() => { editable = editMode; });
32
+ // Editability is decided by the gridStack itself, not the parent.
33
+ // The signal that a parent cares about edits is `onLayoutChange` —
34
+ // no callback means view-only, even on a wide screen. Width gates
35
+ // the rest: drag handles aren't usable on a tiny grid.
36
+ $effect(() => {
37
+ editable = !!onLayoutChange && containerWidth >= 768;
38
+ });
41
39
 
42
40
  function getResponsiveColumns(width: number): number {
43
- if (width >= 1200) return 12;
44
- if (width >= 768) return 6;
45
- if (width >= 480) return 2;
41
+ if (width >= 768) return 12;
46
42
  return 1;
47
43
  }
48
44
 
@@ -63,7 +59,7 @@
63
59
  }
64
60
 
65
61
  $effect(() => {
66
- const mode = editMode; // force-read so always tracked
62
+ const mode = editable; // force-read so always tracked
67
63
  const ready = initialized; // force-read so always tracked
68
64
  if (!grid || !ready) return;
69
65
  if (mode) {
@@ -101,7 +97,7 @@
101
97
  window.dispatchEvent(new Event("resize"));
102
98
  lastWidth = entries[0]?.contentRect.width ?? gridEl!.offsetWidth;
103
99
  containerWidth = lastWidth;
104
- if (!editMode) {
100
+ if (!editable) {
105
101
  grid?.column(getResponsiveColumns(lastWidth));
106
102
  }
107
103
  });
@@ -132,6 +128,7 @@
132
128
  .grid-stack-wrap {
133
129
  overflow-x: hidden;
134
130
  width: 100%;
131
+ height: 100%;
135
132
  }
136
133
 
137
134
  :global(.grid-stack) {
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- import { CanAccess } from "@lobb-js/studio";
2
+ import { CanAccess, isHidden } from "@lobb-js/studio";
3
3
  import ReportBody from "../../../reportBody.svelte";
4
4
 
5
5
  const { reportId, utils }: { reportId: string; utils: any } = $props();
@@ -99,7 +99,7 @@
99
99
  <div class="flex gap-2">
100
100
  <div class="mt-1"><SidebarTrigger /></div>
101
101
  <div class="flex flex-col justify-center">
102
- <h2 class="font-medium text-primary">{report?.name ?? ""}</h2>
102
+ <h2 class="font-medium text-foreground">{report?.name ?? ""}</h2>
103
103
  <div class="text-xs text-muted-foreground">{report?.description ?? ""}</div>
104
104
  </div>
105
105
  </div>
@@ -120,23 +120,25 @@
120
120
  Expand window to rearrange
121
121
  </div>
122
122
  {/if}
123
- <CanAccess collection="auth_shares" action="create">
124
- <Button
125
- variant="outline"
126
- class="h-7 px-3 text-xs font-normal"
127
- Icon={sharing ? Icons.LoaderCircle : Icons.Share2}
128
- disabled={sharing}
129
- onclick={handleShare}
130
- >
131
- {sharing ? "Sharing..." : "Share"}
132
- </Button>
133
- </CanAccess>
123
+ {#if !isHidden(utils.ctx, "reports.dashboardShareButton")}
124
+ <CanAccess collection="auth_shares" action="create">
125
+ <Button
126
+ variant="outline"
127
+ size="sm"
128
+ Icon={sharing ? Icons.LoaderCircle : Icons.Share2}
129
+ disabled={sharing}
130
+ onclick={handleShare}
131
+ >
132
+ {sharing ? "Sharing..." : "Share"}
133
+ </Button>
134
+ </CanAccess>
135
+ {/if}
134
136
  {#if report}
135
137
  <CreateDetailViewButton
136
138
  collectionName="reports_charts"
137
139
  values={{ report_id: { id: reportId, name: report.name } }}
138
140
  variant="default"
139
- class="h-7 px-3 text-xs font-normal"
141
+ size="sm"
140
142
  Icon={Icons.Plus}
141
143
  onSuccessfullSave={async () => await body?.reload()}
142
144
  >
@@ -150,7 +152,7 @@
150
152
  <ReportBody
151
153
  {reportId}
152
154
  {utils}
153
- allowEdit
155
+ bind:editable
154
156
  onLayoutChange={handleLayoutChange}
155
157
  onChartClick={handleChartClick}
156
158
  onReportLoaded={handleReportLoaded}
@@ -131,7 +131,7 @@
131
131
  <CreateDetailViewButton
132
132
  collectionName="reports_dashboards"
133
133
  variant="default"
134
- class="h-7 px-3 text-xs font-normal"
134
+ size="sm"
135
135
  Icon={icons.Plus}
136
136
  onSuccessfullSave={async (record: any) =>
137
137
  utils.goto(`?report=${record.id}`)}
@@ -75,7 +75,7 @@
75
75
  {:else}
76
76
  <div class="flex shrink-0 items-center justify-between gap-2 border-b bg-background p-2">
77
77
  <div class="flex flex-col justify-center">
78
- <h2 class="font-medium text-primary">{report?.name ?? ""}</h2>
78
+ <h2 class="font-medium text-foreground">{report?.name ?? ""}</h2>
79
79
  <div class="text-xs text-muted-foreground">{report?.description ?? ""}</div>
80
80
  </div>
81
81
  <div class="text-xs text-muted-foreground">Shared view</div>
@@ -7,7 +7,6 @@
7
7
  interface Props extends ExtensionProps {
8
8
  reportId: string | number;
9
9
  editable?: boolean;
10
- allowEdit?: boolean;
11
10
  showChartHeaders?: boolean;
12
11
  onLayoutChange?: (changes: any[]) => void;
13
12
  onChartClick?: (event: any, elements: any[], chart: any) => void;
@@ -15,10 +14,9 @@
15
14
  onReady?: (api: { reload: () => Promise<void> }) => void;
16
15
  }
17
16
 
18
- const {
17
+ let {
19
18
  reportId,
20
- editable = false,
21
- allowEdit = false,
19
+ editable = $bindable(false),
22
20
  showChartHeaders = true,
23
21
  onLayoutChange,
24
22
  onChartClick,
@@ -95,7 +93,7 @@
95
93
  </div>
96
94
  {:else}
97
95
  {#key charts}
98
- <GridStackComponent {editable} {allowEdit} {onLayoutChange}>
96
+ <GridStackComponent bind:editable {onLayoutChange}>
99
97
  {#each [...charts].sort((a, b) => (a.sort_order ?? 0) - (b.sort_order ?? 0)) as chart (chart.id)}
100
98
  <div
101
99
  class="grid-stack-item"
@@ -8,7 +8,7 @@
8
8
  const componentProps: any = {
9
9
  title: "Generate the query with AI",
10
10
  description: "Describe to the AI the query you want to generate",
11
- class: "h-7 px-3 text-xs font-normal",
11
+ size: "sm",
12
12
  variant: "outline",
13
13
  // format: {
14
14
  // type: 'json_object'
@@ -9,16 +9,14 @@
9
9
  column?: number;
10
10
  cellHeight?: string;
11
11
  editable?: boolean;
12
- allowEdit?: boolean;
13
12
  onLayoutChange?: (layout: LayoutItem[]) => void;
14
13
  children: Snippet;
15
14
  }
16
15
 
17
16
  let {
18
17
  column = 12,
19
- cellHeight = "150px",
18
+ cellHeight = "50px",
20
19
  editable = $bindable(false),
21
- allowEdit = false,
22
20
  onLayoutChange,
23
21
  children,
24
22
  }: Props = $props();
@@ -31,18 +29,16 @@
31
29
  let containerWidth = $state(0);
32
30
  let saveTimeout: ReturnType<typeof setTimeout> | null = null;
33
31
 
34
- // Edit mode only ever turns on when the parent explicitly opts in.
35
- // Without allowEdit, container width is irrelevant the grid stays
36
- // disabled forever.
37
- let editMode = $derived(allowEdit && containerWidth >= 1200);
38
-
39
- // Keep editable in sync so parent can read it
40
- $effect(() => { editable = editMode; });
32
+ // Editability is decided by the gridStack itself, not the parent.
33
+ // The signal that a parent cares about edits is `onLayoutChange` —
34
+ // no callback means view-only, even on a wide screen. Width gates
35
+ // the rest: drag handles aren't usable on a tiny grid.
36
+ $effect(() => {
37
+ editable = !!onLayoutChange && containerWidth >= 768;
38
+ });
41
39
 
42
40
  function getResponsiveColumns(width: number): number {
43
- if (width >= 1200) return 12;
44
- if (width >= 768) return 6;
45
- if (width >= 480) return 2;
41
+ if (width >= 768) return 12;
46
42
  return 1;
47
43
  }
48
44
 
@@ -63,7 +59,7 @@
63
59
  }
64
60
 
65
61
  $effect(() => {
66
- const mode = editMode; // force-read so always tracked
62
+ const mode = editable; // force-read so always tracked
67
63
  const ready = initialized; // force-read so always tracked
68
64
  if (!grid || !ready) return;
69
65
  if (mode) {
@@ -101,7 +97,7 @@
101
97
  window.dispatchEvent(new Event("resize"));
102
98
  lastWidth = entries[0]?.contentRect.width ?? gridEl!.offsetWidth;
103
99
  containerWidth = lastWidth;
104
- if (!editMode) {
100
+ if (!editable) {
105
101
  grid?.column(getResponsiveColumns(lastWidth));
106
102
  }
107
103
  });
@@ -132,6 +128,7 @@
132
128
  .grid-stack-wrap {
133
129
  overflow-x: hidden;
134
130
  width: 100%;
131
+ height: 100%;
135
132
  }
136
133
 
137
134
  :global(.grid-stack) {
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- import { CanAccess } from "@lobb-js/studio";
2
+ import { CanAccess, isHidden } from "@lobb-js/studio";
3
3
  import ReportBody from "../../../reportBody.svelte";
4
4
 
5
5
  const { reportId, utils }: { reportId: string; utils: any } = $props();
@@ -99,7 +99,7 @@
99
99
  <div class="flex gap-2">
100
100
  <div class="mt-1"><SidebarTrigger /></div>
101
101
  <div class="flex flex-col justify-center">
102
- <h2 class="font-medium text-primary">{report?.name ?? ""}</h2>
102
+ <h2 class="font-medium text-foreground">{report?.name ?? ""}</h2>
103
103
  <div class="text-xs text-muted-foreground">{report?.description ?? ""}</div>
104
104
  </div>
105
105
  </div>
@@ -120,23 +120,25 @@
120
120
  Expand window to rearrange
121
121
  </div>
122
122
  {/if}
123
- <CanAccess collection="auth_shares" action="create">
124
- <Button
125
- variant="outline"
126
- class="h-7 px-3 text-xs font-normal"
127
- Icon={sharing ? Icons.LoaderCircle : Icons.Share2}
128
- disabled={sharing}
129
- onclick={handleShare}
130
- >
131
- {sharing ? "Sharing..." : "Share"}
132
- </Button>
133
- </CanAccess>
123
+ {#if !isHidden(utils.ctx, "reports.dashboardShareButton")}
124
+ <CanAccess collection="auth_shares" action="create">
125
+ <Button
126
+ variant="outline"
127
+ size="sm"
128
+ Icon={sharing ? Icons.LoaderCircle : Icons.Share2}
129
+ disabled={sharing}
130
+ onclick={handleShare}
131
+ >
132
+ {sharing ? "Sharing..." : "Share"}
133
+ </Button>
134
+ </CanAccess>
135
+ {/if}
134
136
  {#if report}
135
137
  <CreateDetailViewButton
136
138
  collectionName="reports_charts"
137
139
  values={{ report_id: { id: reportId, name: report.name } }}
138
140
  variant="default"
139
- class="h-7 px-3 text-xs font-normal"
141
+ size="sm"
140
142
  Icon={Icons.Plus}
141
143
  onSuccessfullSave={async () => await body?.reload()}
142
144
  >
@@ -150,7 +152,7 @@
150
152
  <ReportBody
151
153
  {reportId}
152
154
  {utils}
153
- allowEdit
155
+ bind:editable
154
156
  onLayoutChange={handleLayoutChange}
155
157
  onChartClick={handleChartClick}
156
158
  onReportLoaded={handleReportLoaded}
@@ -131,7 +131,7 @@
131
131
  <CreateDetailViewButton
132
132
  collectionName="reports_dashboards"
133
133
  variant="default"
134
- class="h-7 px-3 text-xs font-normal"
134
+ size="sm"
135
135
  Icon={icons.Plus}
136
136
  onSuccessfullSave={async (record: any) =>
137
137
  utils.goto(`?report=${record.id}`)}
@@ -75,7 +75,7 @@
75
75
  {:else}
76
76
  <div class="flex shrink-0 items-center justify-between gap-2 border-b bg-background p-2">
77
77
  <div class="flex flex-col justify-center">
78
- <h2 class="font-medium text-primary">{report?.name ?? ""}</h2>
78
+ <h2 class="font-medium text-foreground">{report?.name ?? ""}</h2>
79
79
  <div class="text-xs text-muted-foreground">{report?.description ?? ""}</div>
80
80
  </div>
81
81
  <div class="text-xs text-muted-foreground">Shared view</div>
@@ -7,7 +7,6 @@
7
7
  interface Props extends ExtensionProps {
8
8
  reportId: string | number;
9
9
  editable?: boolean;
10
- allowEdit?: boolean;
11
10
  showChartHeaders?: boolean;
12
11
  onLayoutChange?: (changes: any[]) => void;
13
12
  onChartClick?: (event: any, elements: any[], chart: any) => void;
@@ -15,10 +14,9 @@
15
14
  onReady?: (api: { reload: () => Promise<void> }) => void;
16
15
  }
17
16
 
18
- const {
17
+ let {
19
18
  reportId,
20
- editable = false,
21
- allowEdit = false,
19
+ editable = $bindable(false),
22
20
  showChartHeaders = true,
23
21
  onLayoutChange,
24
22
  onChartClick,
@@ -95,7 +93,7 @@
95
93
  </div>
96
94
  {:else}
97
95
  {#key charts}
98
- <GridStackComponent {editable} {allowEdit} {onLayoutChange}>
96
+ <GridStackComponent bind:editable {onLayoutChange}>
99
97
  {#each [...charts].sort((a, b) => (a.sort_order ?? 0) - (b.sort_order ?? 0)) as chart (chart.id)}
100
98
  <div
101
99
  class="grid-stack-item"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobb-js/lobb-ext-reports",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
4
4
  "license": "UNLICENSED",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -32,7 +32,7 @@
32
32
  "package": "svelte-package --input extensions/reports/studio"
33
33
  },
34
34
  "dependencies": {
35
- "@lobb-js/core": "^0.34.0",
35
+ "@lobb-js/core": "^0.36.0",
36
36
  "chart.js": "^4.4.8",
37
37
  "gridstack": "^12.6.0",
38
38
  "hono": "^4.7.0",
@@ -43,7 +43,7 @@
43
43
  },
44
44
  "devDependencies": {
45
45
  "@faker-js/faker": "^9.6.0",
46
- "@lobb-js/studio": "^0.33.0",
46
+ "@lobb-js/studio": "^0.36.0",
47
47
  "@lucide/svelte": "^0.563.1",
48
48
  "@sveltejs/adapter-node": "^5.5.4",
49
49
  "@sveltejs/kit": "^2.60.1",