@likable-hair/svelte 4.1.6 → 4.2.1

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.
Files changed (54) hide show
  1. package/dist/components/composed/common/HeadersDrawer.svelte +38 -6
  2. package/dist/components/composed/common/HeadersDrawer.svelte.d.ts +1 -0
  3. package/dist/components/composed/common/MenuOrDrawer.svelte +1 -1
  4. package/dist/components/composed/common/MenuOrDrawerOptions.svelte +1 -1
  5. package/dist/components/composed/dashboard/DashboardShaper.css +11 -0
  6. package/dist/components/composed/dashboard/DashboardShaper.svelte +421 -0
  7. package/dist/components/composed/dashboard/DashboardShaper.svelte.d.ts +150 -0
  8. package/dist/components/composed/forms/AsyncAutocomplete.svelte.d.ts +1 -0
  9. package/dist/components/composed/forms/DatePickerTextField.svelte +10 -2
  10. package/dist/components/composed/forms/DatePickerTextField.svelte.d.ts +1 -0
  11. package/dist/components/composed/forms/Dropdown.svelte +15 -6
  12. package/dist/components/composed/forms/PeriodPicker.svelte +111 -0
  13. package/dist/components/composed/forms/PeriodPicker.svelte.d.ts +21 -0
  14. package/dist/components/composed/forms/PeriodSelector.svelte +621 -0
  15. package/dist/components/composed/forms/PeriodSelector.svelte.d.ts +59 -0
  16. package/dist/components/composed/list/DynamicTable.css +4 -0
  17. package/dist/components/composed/list/DynamicTable.svelte +280 -83
  18. package/dist/components/composed/list/DynamicTable.svelte.d.ts +3 -9
  19. package/dist/components/composed/list/EnhancedPaginatedTable.css +3 -0
  20. package/dist/components/composed/list/EnhancedPaginatedTable.svelte +41 -11
  21. package/dist/components/composed/list/EnhancedPaginatedTable.svelte.d.ts +7 -5
  22. package/dist/components/composed/list/PaginatedTable.svelte +19 -17
  23. package/dist/components/composed/list/PaginatedTable.svelte.d.ts +5 -5
  24. package/dist/components/composed/search/DynamicFilters.svelte +10 -10
  25. package/dist/components/composed/search/DynamicFilters.svelte.d.ts +6 -0
  26. package/dist/components/composed/search/FilterEditor.svelte +10 -9
  27. package/dist/components/composed/search/Filters.svelte +1 -1
  28. package/dist/components/simple/buttons/Button.css +1 -0
  29. package/dist/components/simple/buttons/Button.svelte +4 -1
  30. package/dist/components/simple/common/Menu.svelte +124 -104
  31. package/dist/components/simple/common/Menu.svelte.d.ts +2 -0
  32. package/dist/components/simple/common/VerticalDraggableList.css +3 -1
  33. package/dist/components/simple/common/VerticalDraggableList.svelte +71 -6
  34. package/dist/components/simple/common/VerticalDraggableList.svelte.d.ts +5 -0
  35. package/dist/components/simple/dates/Calendar.css +2 -2
  36. package/dist/components/simple/dates/Calendar.svelte +76 -27
  37. package/dist/components/simple/dates/Calendar.svelte.d.ts +1 -0
  38. package/dist/components/simple/dates/DatePicker.svelte +30 -12
  39. package/dist/components/simple/dates/DatePicker.svelte.d.ts +8 -1
  40. package/dist/components/simple/forms/Autocomplete.svelte +2 -2
  41. package/dist/components/simple/forms/Autocomplete.svelte.d.ts +1 -0
  42. package/dist/components/simple/forms/Textarea.css +17 -0
  43. package/dist/components/simple/forms/Textarea.svelte +99 -37
  44. package/dist/components/simple/forms/Textarea.svelte.d.ts +9 -24
  45. package/dist/components/simple/lists/SelectableVerticalList.svelte +5 -4
  46. package/dist/components/simple/lists/SimpleTable.css +8 -2
  47. package/dist/components/simple/lists/SimpleTable.svelte +354 -135
  48. package/dist/components/simple/lists/SimpleTable.svelte.d.ts +6 -4
  49. package/dist/components/simple/media/Icon.svelte +1 -1
  50. package/dist/components/simple/navigation/Chip.css +4 -0
  51. package/dist/components/simple/navigation/Chip.svelte +40 -18
  52. package/dist/index.d.ts +3 -1
  53. package/dist/index.js +3 -1
  54. package/package.json +1 -1
@@ -3,7 +3,9 @@ import { flip } from "svelte/animate";
3
3
  import { quintOut } from "svelte/easing";
4
4
  import { crossfade } from "svelte/transition";
5
5
  import EnhancedPaginatedTable from "../list/EnhancedPaginatedTable.svelte";
6
- let { drawerProps, open = $bindable(), lang, availableHeaders, headersToShow = $bindable(), onsaveHeadersToShow, itemSnippet: internalItemSnippet, headersToAddSnippet, contentSnippet, } = $props();
6
+ import lodash from "lodash";
7
+ const deepClone = lodash.cloneDeep;
8
+ let { drawerProps, open = $bindable(), lang, availableHeaders, headersToShow = $bindable(), pinnableColumns, onsaveHeadersToShow, itemSnippet: internalItemSnippet, headersToAddSnippet, contentSnippet, } = $props();
7
9
  const [send, receive] = crossfade({
8
10
  duration: 500,
9
11
  fallback(node, params) {
@@ -19,9 +21,9 @@ const [send, receive] = crossfade({
19
21
  };
20
22
  },
21
23
  });
22
- let internalHeadersToShow = $state(headersToShow);
24
+ let internalHeadersToShow = $state(deepClone(headersToShow));
23
25
  $effect(() => {
24
- internalHeadersToShow = headersToShow;
26
+ internalHeadersToShow = deepClone(headersToShow);
25
27
  });
26
28
  function saveHeadersToShow() {
27
29
  headersToShow = internalHeadersToShow;
@@ -58,6 +60,7 @@ function saveHeadersToShow() {
58
60
  ...e,
59
61
  id: e.value,
60
62
  name: e.label,
63
+ pinned: e.pinned,
61
64
  }
62
65
  })}
63
66
  onchangeOrder={(e) => {
@@ -71,15 +74,17 @@ function saveHeadersToShow() {
71
74
  internalHeadersToShow = newHeaders;
72
75
  }}
73
76
  >
74
- {#snippet itemSnippet({ item })}
77
+ {#snippet itemSnippet({ item, index })}
75
78
  {#if internalItemSnippet}
76
- {@render internalItemSnippet({ item })}
79
+ {@render internalItemSnippet({ item, index })}
77
80
  {:else}
81
+ {@const locked = internalHeadersToShow.find((h) => h.value == item.id)?.locked}
78
82
  <div
79
83
  style:display=flex
80
84
  >
81
85
  <div
82
86
  style:flex-grow=1
87
+ style:opacity={item.pinned ? 0.8 : 1}
83
88
  >
84
89
  {#if !!item.icon}
85
90
  <Icon name={item.icon} />
@@ -90,10 +95,12 @@ function saveHeadersToShow() {
90
95
  style:display=flex
91
96
  style:min-width=50px
92
97
  style:justify-content=end
98
+ style:gap=6px
93
99
  >
94
100
  <Switch
95
101
  --switch-label-width="90%"
96
102
  value={internalHeadersToShow.find((h) => h.value == item.id) != undefined}
103
+ disabled={!!item.pinned}
97
104
  onchange={(e) => {
98
105
  if (e.detail.value == false) {
99
106
  internalHeadersToShow = internalHeadersToShow.filter((h) => h.value != item.id);
@@ -101,6 +108,31 @@ function saveHeadersToShow() {
101
108
  }
102
109
  }}
103
110
  />
111
+ {#if pinnableColumns || item.pinned}
112
+ <div style:opacity={item.locked ? 0.8 : 1}>
113
+ <Icon
114
+ name={item.pinned ? 'mdi-pin-off' : 'mdi-pin'}
115
+ onclick={
116
+ !locked ?
117
+ () => {
118
+ let header = internalHeadersToShow.find((h) => h.value == item.id);
119
+ if (header) {
120
+ header.pinned = !header.pinned;
121
+ let pinnedHeaders = internalHeadersToShow.filter((h) => h.pinned);
122
+ let unpinnedHeaders = internalHeadersToShow.filter((h) => !h.pinned);
123
+ internalHeadersToShow = [
124
+ ...pinnedHeaders,
125
+ ...unpinnedHeaders
126
+ ];
127
+ }
128
+ } :
129
+ undefined
130
+ }
131
+ --icon-cursor={locked ? 'not-allowed' : ''}
132
+ --icon-size=17px
133
+ ></Icon>
134
+ </div>
135
+ {/if}
104
136
  </div>
105
137
  </div>
106
138
  {/if}
@@ -113,7 +145,7 @@ function saveHeadersToShow() {
113
145
  <span class="headers-show">{lang == 'en' ? 'Available headers' : 'Intestazioni disponibili'}</span>
114
146
 
115
147
  {#if availableHeaders && availableHeaders.length > 0}
116
- {#each availableHeaders as header (header.value)}
148
+ {#each availableHeaders.filter(h => !h.pinned) as header (header.value)}
117
149
  <div
118
150
  animate:flip
119
151
  in:receive={{ key: header }}
@@ -9,6 +9,7 @@ declare class __sveltets_Render<Item extends {
9
9
  lang: "en" | "it";
10
10
  availableHeaders: import("../../simple/lists/SimpleTable.svelte").Header<Data>[];
11
11
  headersToShow: import("../../simple/lists/SimpleTable.svelte").Header<Data>[];
12
+ pinnableColumns?: boolean;
12
13
  onsaveHeadersToShow?: ((event: {
13
14
  detail: {
14
15
  headersToShow: import("../../simple/lists/SimpleTable.svelte").Header<Data>[];
@@ -3,7 +3,7 @@ import Drawer from '../../simple/navigation/Drawer.svelte';
3
3
  import Menu from '../../simple/common/Menu.svelte';
4
4
  import MediaQuery from '../../simple/common/MediaQuery.svelte';
5
5
  import lodash from 'lodash';
6
- let { open = $bindable(false), drawerProps, menuProps, children, } = $props();
6
+ let { open = $bindable(), drawerProps, menuProps, children, } = $props();
7
7
  const menuPropsDefaultValue = {
8
8
  closeOnClickOutside: true,
9
9
  _boxShadow: "rgb(var(--global-color-grey-900), .5) 0px 2px 4px",
@@ -5,7 +5,7 @@
5
5
  import MenuOrDrawer from "./MenuOrDrawer.svelte";
6
6
  import SelectableVerticalList from '../../simple/lists/SelectableVerticalList.svelte';
7
7
  import lodash from 'lodash';
8
- let { open = $bindable(false), elements = [], onselect, menuProps, drawerProps, } = $props();
8
+ let { open = $bindable(), elements = [], onselect, menuProps, drawerProps, } = $props();
9
9
  const menuPropsDefaultValue = {
10
10
  anchor: 'bottom-center',
11
11
  stayInViewport: true,
@@ -0,0 +1,11 @@
1
+ :root {
2
+ --dashboard-shaper-default-widget-height: 200px;
3
+ --dashboard-shaper-default-gap: 4px;
4
+ --dashboard-shaper-default-cell-preview-background-color: rgb(var(--global-color-primary-500), 0.5);
5
+ --dashboard-shaper-default-widget-preview-border-radius: 8px;
6
+ --dashboard-shaper-default-display-mobile: flex;
7
+ --dashboard-shaper-default-flex-direction-mobile: column;
8
+ --dashboard-shaper-default-dialog-background-color: rgb(var(--global-color-background-300));
9
+ --dashboard-shaper-default-dialog-header-background-color: rgb(var(--global-color-background-400));
10
+ --dashboard-shaper-default-add-widget-button-background-color: rgb(var(--global-color-background-300));
11
+ }
@@ -0,0 +1,421 @@
1
+ <script lang="ts">import './DashboardShaper.css';
2
+ import lodash from 'lodash';
3
+ import { Dialog, Icon } from "../../..";
4
+ import { createId } from "@paralleldrive/cuid2";
5
+ import Chip from "../../simple/navigation/Chip.svelte";
6
+ const deepEqual = lodash.isEqual;
7
+ let { layoutWidth = 6, layoutHeight = 4, widgets = $bindable([]), availableWidgetCells, someRowSlotEmpty = $bindable(true), preview = false, canAdd = true, lang = 'en', onaddWidgetCell, onremoveWidgetCell, widgetCellSnippet, widgetSelectionDialogHeaderSnippet, addWidgetButtonSnippet, } = $props();
8
+ let widgetCells = $state([]);
9
+ let normalizedWidgetGrid = $derived.by(() => {
10
+ return computeNormalizedWidgetGrid(widgetCells || [], layoutWidth, layoutHeight);
11
+ }), filledWidgetGrid = $derived.by(() => {
12
+ return computeFilledWidgetGrid(normalizedWidgetGrid || [], layoutWidth, layoutHeight);
13
+ }), addWidgetDialog = $state(false), addWidgetInfo = $state();
14
+ $effect(() => {
15
+ if (widgets) {
16
+ calculateLocalWidgetsFromWidgets();
17
+ }
18
+ });
19
+ function calculateLocalWidgetsFromWidgets() {
20
+ widgetCells = widgets?.map((w) => {
21
+ return {
22
+ widget: {
23
+ id: w.id,
24
+ componentName: w.componentName,
25
+ height: w.height,
26
+ width: w.width,
27
+ left: w.left,
28
+ top: w.top,
29
+ },
30
+ columnSpanFrom: w.left,
31
+ columnSpanTo: w.left + w.width,
32
+ rowSpanFrom: w.top,
33
+ rowSpanTo: w.top + w.height,
34
+ };
35
+ });
36
+ }
37
+ $effect(() => {
38
+ const newVal = (normalizedWidgetGrid || []).some((row) => {
39
+ for (let i = 0; i < layoutWidth; i += 1) {
40
+ if (row[i] === undefined)
41
+ return true;
42
+ }
43
+ return false;
44
+ });
45
+ if (newVal !== someRowSlotEmpty) {
46
+ someRowSlotEmpty = newVal;
47
+ }
48
+ });
49
+ function computeNormalizedWidgetGrid(cells, layoutWidthParam, layoutHeightParam) {
50
+ const grid = [];
51
+ for (let i = 0; i < cells.length; i++) {
52
+ const widgetCell = cells[i];
53
+ const rowFrom = widgetCell.rowSpanFrom - 1;
54
+ const rowTo = widgetCell.rowSpanTo - 1;
55
+ const colFrom = widgetCell.columnSpanFrom - 1;
56
+ const colTo = widgetCell.columnSpanTo - 1;
57
+ for (let r = rowFrom; r < rowTo; r++) {
58
+ if (!grid[r])
59
+ grid[r] = [];
60
+ for (let c = colFrom; c < colTo; c++) {
61
+ grid[r][c] = widgetCell;
62
+ }
63
+ }
64
+ }
65
+ for (let r = 0; r < grid.length; r++) {
66
+ if (!grid[r])
67
+ grid[r] = [];
68
+ for (let c = 0; c < layoutWidthParam; c++) {
69
+ if (grid[r][c] === undefined)
70
+ grid[r][c] = undefined;
71
+ }
72
+ }
73
+ return grid;
74
+ }
75
+ function computeFilledWidgetGrid(normGrid, layoutWidthParam, layoutHeightParam) {
76
+ const filled = [];
77
+ for (let r = 0; r < normGrid.length; r++) {
78
+ const normalizedRow = normGrid[r] ?? [];
79
+ for (let c = 0; c < layoutWidthParam; c++) {
80
+ const rowItem = normalizedRow[c];
81
+ if (rowItem === undefined && (normalizedRow[c - 1] !== undefined || c === 0)) {
82
+ let availableHeight = 1;
83
+ while (normGrid[r + availableHeight]?.[c] === undefined &&
84
+ r + availableHeight < layoutHeightParam) {
85
+ availableHeight += 1;
86
+ }
87
+ let availableWidth = 1;
88
+ while (normGrid[r]?.[c + availableWidth] === undefined &&
89
+ c + availableWidth < layoutWidthParam) {
90
+ availableWidth += 1;
91
+ }
92
+ filled.push({
93
+ rowSpanFrom: r + 1,
94
+ rowSpanTo: r + 2,
95
+ columnSpanFrom: c + 1,
96
+ columnSpanTo: c + availableWidth + 1,
97
+ availableHeight,
98
+ availableWidth
99
+ });
100
+ }
101
+ else if (rowItem !== undefined &&
102
+ filled.every((w) => w.widget?.id !== rowItem?.widget.id)) {
103
+ filled.push(rowItem);
104
+ }
105
+ }
106
+ }
107
+ return filled;
108
+ }
109
+ function handleAddClick(params) {
110
+ addWidgetDialog = true;
111
+ addWidgetInfo = {
112
+ availableHeight: params.slot.availableHeight || 1,
113
+ availableWidth: params.slot.availableWidth || 1,
114
+ fromRow: params.slot.rowSpanFrom,
115
+ fromColumn: params.slot.columnSpanFrom
116
+ };
117
+ }
118
+ function addWidgetCell(params) {
119
+ const widgetCellToAdd = {
120
+ widget: params.widget,
121
+ rowSpanFrom: params.fromRow,
122
+ rowSpanTo: params.fromRow + params.height,
123
+ columnSpanFrom: params.fromColumn,
124
+ columnSpanTo: params.fromColumn + params.width
125
+ };
126
+ const newCells = [...(widgetCells || []), widgetCellToAdd];
127
+ if (!deepEqual(newCells, widgetCells)) {
128
+ widgetCells = newCells;
129
+ }
130
+ calculateWidgetsFromLocalWidgets();
131
+ onaddWidgetCell?.({
132
+ widgetCell: widgetCellToAdd
133
+ });
134
+ }
135
+ function removeWidgetCell(params) {
136
+ const widgetCellToRemove = (widgetCells || []).find((w) => w.widget.id === params.id);
137
+ const newCells = (widgetCells || []).filter((w) => w.widget.id !== params.id);
138
+ if (!deepEqual(newCells, widgetCells)) {
139
+ widgetCells = newCells;
140
+ }
141
+ calculateWidgetsFromLocalWidgets();
142
+ if (!!widgetCellToRemove) {
143
+ onremoveWidgetCell?.({
144
+ widgetCell: widgetCellToRemove
145
+ });
146
+ }
147
+ }
148
+ function calculateWidgetsFromLocalWidgets() {
149
+ widgets = widgetCells?.map((lWidget) => {
150
+ return {
151
+ id: lWidget.widget.id,
152
+ componentName: lWidget.widget.componentName,
153
+ height: lWidget.widget.height,
154
+ width: lWidget.widget.width,
155
+ left: lWidget.widget.left,
156
+ top: lWidget.widget.top,
157
+ };
158
+ });
159
+ }
160
+ function closeAddWidgetDialog() {
161
+ addWidgetDialog = false;
162
+ }
163
+ function availableSizes(params) {
164
+ return params.availableSizes.filter((as) => as[0] <= params.availableHeight && as[1] <= params.availableWidth);
165
+ }
166
+ </script>
167
+
168
+ <div
169
+ class="shaper"
170
+ style:--dashboard-shaper-grid-template-columns={`repeat(${layoutWidth}, 1fr)`}
171
+ style:--dashboard-shaper-grid-template-rows={`repeat(${layoutHeight}, var(--dashboard-shaper-widget-height, var(--dashboard-shaper-default-widget-height)))`}
172
+ >
173
+ {#each filledWidgetGrid as widgetCell}
174
+ {#if !!widgetCell.widget}
175
+ <div
176
+ style:grid-column={`${widgetCell.columnSpanFrom} / ${widgetCell.columnSpanTo}`}
177
+ style:grid-row={`${widgetCell.rowSpanFrom} / ${widgetCell.rowSpanTo}`}
178
+ >
179
+ {#if preview}
180
+ <div class="widget-cell-preview"></div>
181
+ {:else}
182
+ {#if widgetCellSnippet}
183
+ {@render widgetCellSnippet({ widgetCell, removeWidgetCell })}
184
+ {:else}
185
+ {widgetCell.widget.componentName}
186
+ {/if}
187
+ {/if}
188
+ </div>
189
+ {:else if !preview && canAdd}
190
+ <div
191
+ style:grid-column={`${widgetCell.columnSpanFrom} / ${widgetCell.columnSpanTo}`}
192
+ style:grid-row={`${widgetCell.rowSpanFrom} / ${widgetCell.rowSpanTo}`}
193
+ >
194
+ {@render addWidgetButton(widgetCell)}
195
+ </div>
196
+ {/if}
197
+ {/each}
198
+ {#if normalizedWidgetGrid.length < layoutHeight && !preview && canAdd}
199
+ <div style:grid-column={`1 / ${layoutWidth + 1}`}>
200
+ {@render addWidgetButton({
201
+ columnSpanFrom: 1,
202
+ columnSpanTo: layoutWidth + 1,
203
+ rowSpanFrom: normalizedWidgetGrid.length + 1,
204
+ rowSpanTo: normalizedWidgetGrid.length + 2,
205
+ availableHeight: layoutHeight - normalizedWidgetGrid.length,
206
+ availableWidth: layoutWidth
207
+ })}
208
+ </div>
209
+ {/if}
210
+ </div>
211
+
212
+ {#snippet addWidgetButton(slot: Parameters<typeof handleAddClick>[0]['slot'])}
213
+ {#if addWidgetButtonSnippet}
214
+ {@render addWidgetButtonSnippet({ handleAddClick, slot})}
215
+ {:else}
216
+ <button
217
+ class="add-button"
218
+ onclick={() => handleAddClick({ slot })}
219
+ >
220
+ <Icon name="mdi-plus" --icon-default-size="1.5rem" />
221
+ <div class="description">{lang == 'en' ? 'Add widget' : 'Aggiungi widget'}</div>
222
+ </button>
223
+ {/if}
224
+ {/snippet}
225
+
226
+ <Dialog
227
+ bind:open={addWidgetDialog}
228
+ >
229
+ <div class="card">
230
+ {#if widgetSelectionDialogHeaderSnippet}
231
+ {@render widgetSelectionDialogHeaderSnippet()}
232
+ {:else}
233
+ <div class="header">
234
+ <Icon name={"mdi-widgets-outline"} --icon-size="30px"/>
235
+ <div class="title">{lang == 'en' ? 'Add widget' : 'Aggiungi widget'}</div>
236
+ </div>
237
+ {/if}
238
+ {#key addWidgetInfo}
239
+ {#if !!addWidgetInfo}
240
+ <div class="widget-selection-list">
241
+ {#each availableWidgetCells
242
+ .filter((ws) => availableSizes({
243
+ availableHeight: addWidgetInfo!.availableHeight,
244
+ availableWidth: addWidgetInfo!.availableWidth,
245
+ availableSizes: ws.availableSizes
246
+ }).length > 0)
247
+ .sort((a, b) => {
248
+ if (a.label == 'Placeholder') return -1
249
+ if (a.label < b.label) return -1
250
+ if (a.label > b.label) return 1
251
+ return 0
252
+ }) as widgetSpec
253
+ }
254
+ <div class="widget-card">
255
+ <div class="widget-info">
256
+ {#if widgetSpec.icon}
257
+ <Icon name={widgetSpec.icon} --icon-size="30px" />
258
+ {/if}
259
+ <div class="widget-title">{widgetSpec.label}</div>
260
+ <div class="widget-desc">{widgetSpec.description}</div>
261
+ </div>
262
+
263
+ <div class="widget-actions">
264
+ {#each availableSizes({
265
+ availableHeight: addWidgetInfo.availableHeight,
266
+ availableWidth: addWidgetInfo.availableWidth,
267
+ availableSizes: widgetSpec.availableSizes
268
+ }) as sizes
269
+ }
270
+ <Chip
271
+ inactive
272
+ onclick={() => {
273
+ addWidgetCell({
274
+ widget: {
275
+ id: createId(),
276
+ componentName: widgetSpec.widgetComponentName,
277
+ height: sizes[0],
278
+ width: sizes[1],
279
+ top: addWidgetInfo!.fromRow,
280
+ left: addWidgetInfo!.fromColumn,
281
+ },
282
+ fromColumn: addWidgetInfo!.fromColumn,
283
+ fromRow: addWidgetInfo!.fromRow,
284
+ height: sizes[0],
285
+ width: sizes[1]
286
+ })
287
+ closeAddWidgetDialog()
288
+ }}
289
+ --chip-height=25px
290
+ --chip-min-height=0px
291
+ --chip-padding="2px 4px 0px"
292
+ --chip-inactive-background-color=rgb(var(--global-color-background-500))
293
+ >
294
+ {sizes[0]} x {sizes[1]}
295
+ </Chip>
296
+ {/each}
297
+ </div>
298
+ </div>
299
+ {/each}
300
+ </div>
301
+ {/if}
302
+ {/key}
303
+ </div>
304
+ </Dialog>
305
+
306
+ <style>
307
+ .shaper {
308
+ width: 100%;
309
+ min-height: 200px;
310
+ display: grid;
311
+ gap: var(--dashboard-shaper-gap, var(--dashboard-shaper-default-gap));
312
+ grid-template-columns: var(--dashboard-shaper-grid-template-columns);
313
+ grid-template-rows: var(--dashboard-shaper-grid-template-rows);
314
+ }
315
+
316
+ .widget-cell-preview {
317
+ width: 100%;
318
+ height: 100%;
319
+ background-color: var(--dashboard-shaper-cell-preview-background-color, var(--dashboard-shaper-default-cell-preview-background-color));
320
+ border-radius: var(--dashboard-shaper-widget-preview-border-radius, var(--dashboard-shaper-default-widget-preview-border-radius));
321
+ }
322
+
323
+ @media screen and (max-width: 800px) {
324
+ .shaper {
325
+ display: var(--dashboard-shaper-display-mobile, var(--dashboard-shaper-default-display-mobile));
326
+ flex-direction: var(--dashboard-shaper-flex-direction-mobile, var(--dashboard-shaper-default-flex-direction-mobile));
327
+ }
328
+ }
329
+
330
+ .card {
331
+ background-color: var(--dashboard-shaper-dialog-background-color, var(--dashboard-shaper-default-dialog-background-color));
332
+ border-radius: 8px;
333
+ padding: 0px;
334
+ max-width: 80vw;
335
+ overflow-x: auto;
336
+ }
337
+
338
+ .header {
339
+ display: flex;
340
+ padding: 30px 24px 16px;
341
+ gap: 12px;
342
+ background-color: var(--dashboard-shaper-dialog-header-background-color, var(--dashboard-shaper-default-dialog-header-background-color));
343
+ border-block-end: 1px solid rgb(var(--global-color-contrast-100));
344
+ height: 35px;
345
+ }
346
+
347
+ .title {
348
+ font-size: x-large;
349
+ font-weight: bold;
350
+ text-align: center;
351
+ }
352
+
353
+ .add-button {
354
+ width: 100%;
355
+ height: 100%;
356
+ min-height: 200px;
357
+ display: flex;
358
+ flex-direction: column;
359
+ align-items: center;
360
+ justify-content: center;
361
+ background-color: var(--dashboard-shaper-add-widget-button-background-color, var(--dashboard-shaper-default-add-widget-button-background-color));
362
+ border-radius: 8px;
363
+ }
364
+
365
+ .description {
366
+ font-size: 0.9rem;
367
+ font-weight: 300;
368
+ opacity: 0.7;
369
+ }
370
+
371
+ .widget-selection-list {
372
+ display: flex;
373
+ flex-wrap: wrap;
374
+ max-width: 800px;
375
+ padding: 1rem;
376
+ max-height: 70vh;
377
+ overflow-y: auto;
378
+ }
379
+
380
+ @media screen and (max-width: 800px) {
381
+ .widget-selection-list {
382
+ width: 100%;
383
+ flex-direction: column;
384
+ flex-wrap: nowrap;
385
+ }
386
+ }
387
+
388
+ .widget-card {
389
+ padding: 0.625rem;
390
+ border-radius: 0.375rem;
391
+ display: flex;
392
+ flex-direction: column;
393
+ position: relative;
394
+ flex-basis: 50%;
395
+ box-sizing: border-box;
396
+ }
397
+
398
+ .widget-info {
399
+ display: flex;
400
+ flex-direction: column;
401
+ gap: 0.5rem;
402
+ }
403
+
404
+ .widget-title {
405
+ font-size: 1.25rem;
406
+ line-height: 1.75rem;
407
+ font-weight: 700;
408
+ }
409
+
410
+ .widget-desc {
411
+ font-size: 0.875rem;
412
+ font-weight: 300;
413
+ }
414
+
415
+ .widget-actions {
416
+ display: flex;
417
+ gap: 0.5rem;
418
+ flex-wrap: wrap;
419
+ margin-top: 0.5rem;
420
+ }
421
+ </style>