@lobb-js/lobb-ext-reports 0.13.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,6 +1,6 @@
1
1
  <script lang="ts">
2
- import { onMount } from "svelte";
3
2
  import type { ExtensionProps } from "@lobb-js/studio";
3
+ import { CanAccess } from "@lobb-js/studio";
4
4
  import Table from "./charts/table.svelte";
5
5
  import ChartJs from "./charts/chartJs.svelte";
6
6
  import Metric from "./charts/metric.svelte";
@@ -33,27 +33,6 @@
33
33
  const { UpdateDetailViewButton, Button, Tooltip } = utils.components;
34
34
  const icons = utils.components.Icons;
35
35
 
36
- // Button visibility comes from the caller's actual permissions on
37
- // reports_charts. A logged-in admin sees edit + delete; a viewer without
38
- // those grants (including a share-token recipient) doesn't see either.
39
- let canUpdate = $state(false);
40
- let canDelete = $state(false);
41
-
42
- onMount(async () => {
43
- const [updateAllowed, deleteAllowed] = await Promise.all([
44
- utils.emitEvent("auth.canAccess", {
45
- collection: "reports_charts",
46
- action: "update",
47
- }),
48
- utils.emitEvent("auth.canAccess", {
49
- collection: "reports_charts",
50
- action: "delete",
51
- }),
52
- ]);
53
- canUpdate = updateAllowed === true;
54
- canDelete = deleteAllowed === true;
55
- });
56
-
57
36
  function findCustomChartComponent(type: string): any {
58
37
  const extensions = utils.ctx?.extensions ?? {};
59
38
  for (const ext of Object.values(extensions) as any[]) {
@@ -116,7 +95,7 @@
116
95
  {/if}
117
96
  </div>
118
97
  <div class="flex">
119
- {#if canUpdate}
98
+ <CanAccess collection="reports_charts" action="update">
120
99
  <UpdateDetailViewButton
121
100
  collectionName="reports_charts"
122
101
  recordId={chartRecord.id}
@@ -125,8 +104,8 @@
125
104
  Icon={icons.Pencil}
126
105
  onSuccessfullSave={handleEditOnSuccessfull}
127
106
  ></UpdateDetailViewButton>
128
- {/if}
129
- {#if canDelete}
107
+ </CanAccess>
108
+ <CanAccess collection="reports_charts" action="delete">
130
109
  <Button
131
110
  class="h-6 w-6 text-muted-foreground hover:bg-transparent"
132
111
  variant="ghost"
@@ -134,7 +113,7 @@
134
113
  Icon={icons.Trash}
135
114
  onclick={handleChartDelete}
136
115
  ></Button>
137
- {/if}
116
+ </CanAccess>
138
117
  </div>
139
118
  </div>
140
119
  {/if}
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- import { onMount } from "svelte";
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();
@@ -13,7 +13,6 @@
13
13
  let report: any = $state(null);
14
14
  let saving = $state(false);
15
15
  let editable = $state(false);
16
- let canShare = $state(false);
17
16
  let sharing = $state(false);
18
17
  // Handle captured from ReportBody via its onReady callback — lets us
19
18
  // trigger a reload of charts after creating a new one without duplicating
@@ -49,14 +48,6 @@
49
48
  });
50
49
  }
51
50
 
52
- async function checkSharePermission() {
53
- const allowed = await utils.emitEvent("auth.canAccess", {
54
- collection: "auth_shares",
55
- action: "create",
56
- });
57
- canShare = allowed === true;
58
- }
59
-
60
51
  async function handleShare() {
61
52
  sharing = true;
62
53
  try {
@@ -101,9 +92,6 @@
101
92
  }
102
93
  }
103
94
 
104
- onMount(() => {
105
- checkSharePermission();
106
- });
107
95
  </script>
108
96
 
109
97
  <div class="report-layout">
@@ -111,7 +99,7 @@
111
99
  <div class="flex gap-2">
112
100
  <div class="mt-1"><SidebarTrigger /></div>
113
101
  <div class="flex flex-col justify-center">
114
- <h2 class="font-medium text-primary">{report?.name ?? ""}</h2>
102
+ <h2 class="font-medium text-foreground">{report?.name ?? ""}</h2>
115
103
  <div class="text-xs text-muted-foreground">{report?.description ?? ""}</div>
116
104
  </div>
117
105
  </div>
@@ -132,23 +120,25 @@
132
120
  Expand window to rearrange
133
121
  </div>
134
122
  {/if}
135
- {#if canShare}
136
- <Button
137
- variant="outline"
138
- class="h-7 px-3 text-xs font-normal"
139
- Icon={sharing ? Icons.LoaderCircle : Icons.Share2}
140
- disabled={sharing}
141
- onclick={handleShare}
142
- >
143
- {sharing ? "Sharing..." : "Share"}
144
- </Button>
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>
145
135
  {/if}
146
136
  {#if report}
147
137
  <CreateDetailViewButton
148
138
  collectionName="reports_charts"
149
139
  values={{ report_id: { id: reportId, name: report.name } }}
150
140
  variant="default"
151
- class="h-7 px-3 text-xs font-normal"
141
+ size="sm"
152
142
  Icon={Icons.Plus}
153
143
  onSuccessfullSave={async () => await body?.reload()}
154
144
  >
@@ -162,7 +152,7 @@
162
152
  <ReportBody
163
153
  {reportId}
164
154
  {utils}
165
- allowEdit
155
+ bind:editable
166
156
  onLayoutChange={handleLayoutChange}
167
157
  onChartClick={handleChartClick}
168
158
  onReportLoaded={handleReportLoaded}
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  import type { ExtensionProps } from "@lobb-js/studio";
3
+ import { ExtensionsComponents } from "@lobb-js/studio";
3
4
  import Report from "./components/report.svelte";
4
5
 
5
6
  const { utils, ...props }: ExtensionProps = $props();
@@ -58,7 +59,7 @@
58
59
 
59
60
  <Sidebar title="Dashboards" data={sideBarData}>
60
61
  {#snippet belowSearch()}
61
- <div class="p-2">
62
+ <div class="flex flex-col gap-2 p-2">
62
63
  <CreateDetailViewButton
63
64
  collectionName="reports_dashboards"
64
65
  variant="outline"
@@ -71,6 +72,16 @@
71
72
  >
72
73
  Create a report
73
74
  </CreateDetailViewButton>
75
+ <!-- Extension hook: apps can register components for
76
+ `reports.sidebar.actions` to drop actions into the
77
+ dashboards sidebar (below Create). The `url` prop is
78
+ forwarded so the consumer's `when` predicate can
79
+ scope by route. -->
80
+ <ExtensionsComponents
81
+ name="reports.sidebar.actions"
82
+ {utils}
83
+ url={utils.page.url}
84
+ />
74
85
  </div>
75
86
  {/snippet}
76
87
  {#snippet elementRightSide(item: any)}
@@ -120,7 +131,7 @@
120
131
  <CreateDetailViewButton
121
132
  collectionName="reports_dashboards"
122
133
  variant="default"
123
- class="h-7 px-3 text-xs font-normal"
134
+ size="sm"
124
135
  Icon={icons.Plus}
125
136
  onSuccessfullSave={async (record: any) =>
126
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,6 +1,6 @@
1
1
  <script lang="ts">
2
- import { onMount } from "svelte";
3
2
  import type { ExtensionProps } from "@lobb-js/studio";
3
+ import { CanAccess } from "@lobb-js/studio";
4
4
  import Table from "./charts/table.svelte";
5
5
  import ChartJs from "./charts/chartJs.svelte";
6
6
  import Metric from "./charts/metric.svelte";
@@ -33,27 +33,6 @@
33
33
  const { UpdateDetailViewButton, Button, Tooltip } = utils.components;
34
34
  const icons = utils.components.Icons;
35
35
 
36
- // Button visibility comes from the caller's actual permissions on
37
- // reports_charts. A logged-in admin sees edit + delete; a viewer without
38
- // those grants (including a share-token recipient) doesn't see either.
39
- let canUpdate = $state(false);
40
- let canDelete = $state(false);
41
-
42
- onMount(async () => {
43
- const [updateAllowed, deleteAllowed] = await Promise.all([
44
- utils.emitEvent("auth.canAccess", {
45
- collection: "reports_charts",
46
- action: "update",
47
- }),
48
- utils.emitEvent("auth.canAccess", {
49
- collection: "reports_charts",
50
- action: "delete",
51
- }),
52
- ]);
53
- canUpdate = updateAllowed === true;
54
- canDelete = deleteAllowed === true;
55
- });
56
-
57
36
  function findCustomChartComponent(type: string): any {
58
37
  const extensions = utils.ctx?.extensions ?? {};
59
38
  for (const ext of Object.values(extensions) as any[]) {
@@ -116,7 +95,7 @@
116
95
  {/if}
117
96
  </div>
118
97
  <div class="flex">
119
- {#if canUpdate}
98
+ <CanAccess collection="reports_charts" action="update">
120
99
  <UpdateDetailViewButton
121
100
  collectionName="reports_charts"
122
101
  recordId={chartRecord.id}
@@ -125,8 +104,8 @@
125
104
  Icon={icons.Pencil}
126
105
  onSuccessfullSave={handleEditOnSuccessfull}
127
106
  ></UpdateDetailViewButton>
128
- {/if}
129
- {#if canDelete}
107
+ </CanAccess>
108
+ <CanAccess collection="reports_charts" action="delete">
130
109
  <Button
131
110
  class="h-6 w-6 text-muted-foreground hover:bg-transparent"
132
111
  variant="ghost"
@@ -134,7 +113,7 @@
134
113
  Icon={icons.Trash}
135
114
  onclick={handleChartDelete}
136
115
  ></Button>
137
- {/if}
116
+ </CanAccess>
138
117
  </div>
139
118
  </div>
140
119
  {/if}
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- import { onMount } from "svelte";
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();
@@ -13,7 +13,6 @@
13
13
  let report: any = $state(null);
14
14
  let saving = $state(false);
15
15
  let editable = $state(false);
16
- let canShare = $state(false);
17
16
  let sharing = $state(false);
18
17
  // Handle captured from ReportBody via its onReady callback — lets us
19
18
  // trigger a reload of charts after creating a new one without duplicating
@@ -49,14 +48,6 @@
49
48
  });
50
49
  }
51
50
 
52
- async function checkSharePermission() {
53
- const allowed = await utils.emitEvent("auth.canAccess", {
54
- collection: "auth_shares",
55
- action: "create",
56
- });
57
- canShare = allowed === true;
58
- }
59
-
60
51
  async function handleShare() {
61
52
  sharing = true;
62
53
  try {
@@ -101,9 +92,6 @@
101
92
  }
102
93
  }
103
94
 
104
- onMount(() => {
105
- checkSharePermission();
106
- });
107
95
  </script>
108
96
 
109
97
  <div class="report-layout">
@@ -111,7 +99,7 @@
111
99
  <div class="flex gap-2">
112
100
  <div class="mt-1"><SidebarTrigger /></div>
113
101
  <div class="flex flex-col justify-center">
114
- <h2 class="font-medium text-primary">{report?.name ?? ""}</h2>
102
+ <h2 class="font-medium text-foreground">{report?.name ?? ""}</h2>
115
103
  <div class="text-xs text-muted-foreground">{report?.description ?? ""}</div>
116
104
  </div>
117
105
  </div>
@@ -132,23 +120,25 @@
132
120
  Expand window to rearrange
133
121
  </div>
134
122
  {/if}
135
- {#if canShare}
136
- <Button
137
- variant="outline"
138
- class="h-7 px-3 text-xs font-normal"
139
- Icon={sharing ? Icons.LoaderCircle : Icons.Share2}
140
- disabled={sharing}
141
- onclick={handleShare}
142
- >
143
- {sharing ? "Sharing..." : "Share"}
144
- </Button>
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>
145
135
  {/if}
146
136
  {#if report}
147
137
  <CreateDetailViewButton
148
138
  collectionName="reports_charts"
149
139
  values={{ report_id: { id: reportId, name: report.name } }}
150
140
  variant="default"
151
- class="h-7 px-3 text-xs font-normal"
141
+ size="sm"
152
142
  Icon={Icons.Plus}
153
143
  onSuccessfullSave={async () => await body?.reload()}
154
144
  >
@@ -162,7 +152,7 @@
162
152
  <ReportBody
163
153
  {reportId}
164
154
  {utils}
165
- allowEdit
155
+ bind:editable
166
156
  onLayoutChange={handleLayoutChange}
167
157
  onChartClick={handleChartClick}
168
158
  onReportLoaded={handleReportLoaded}
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  import type { ExtensionProps } from "@lobb-js/studio";
3
+ import { ExtensionsComponents } from "@lobb-js/studio";
3
4
  import Report from "./components/report.svelte";
4
5
 
5
6
  const { utils, ...props }: ExtensionProps = $props();
@@ -58,7 +59,7 @@
58
59
 
59
60
  <Sidebar title="Dashboards" data={sideBarData}>
60
61
  {#snippet belowSearch()}
61
- <div class="p-2">
62
+ <div class="flex flex-col gap-2 p-2">
62
63
  <CreateDetailViewButton
63
64
  collectionName="reports_dashboards"
64
65
  variant="outline"
@@ -71,6 +72,16 @@
71
72
  >
72
73
  Create a report
73
74
  </CreateDetailViewButton>
75
+ <!-- Extension hook: apps can register components for
76
+ `reports.sidebar.actions` to drop actions into the
77
+ dashboards sidebar (below Create). The `url` prop is
78
+ forwarded so the consumer's `when` predicate can
79
+ scope by route. -->
80
+ <ExtensionsComponents
81
+ name="reports.sidebar.actions"
82
+ {utils}
83
+ url={utils.page.url}
84
+ />
74
85
  </div>
75
86
  {/snippet}
76
87
  {#snippet elementRightSide(item: any)}
@@ -120,7 +131,7 @@
120
131
  <CreateDetailViewButton
121
132
  collectionName="reports_dashboards"
122
133
  variant="default"
123
- class="h-7 px-3 text-xs font-normal"
134
+ size="sm"
124
135
  Icon={icons.Plus}
125
136
  onSuccessfullSave={async (record: any) =>
126
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.13.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.33.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.32.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",