@likable-hair/svelte 4.0.7 → 4.0.8

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.
@@ -28,9 +28,9 @@ onMount(() => {
28
28
  if (tableContainer?.scrollHeight && tableContainer.clientHeight) {
29
29
  hideScrollbar = tableContainer.scrollHeight > tableContainer.clientHeight;
30
30
  }
31
- for (const head of [...headers, { value: 'non-resizable', minWidth: DEFAULT_MIN_WIDTH_PX + 'px', maxWidth: DEFAULT_MAX_WIDTH_PX + 'px' }, { value: 'slot-append', minWidth: DEFAULT_MIN_WIDTH_PX + 'px', maxWidth: DEFAULT_MAX_WIDTH_PX + 'px' }]) {
31
+ for (const head of [...headers, { value: 'non-resizable', minWidth: DEFAULT_MIN_WIDTH_PX + 'px', maxWidth: DEFAULT_MAX_WIDTH_PX + 'px' }, { value: 'customize-headers', minWidth: DEFAULT_MIN_WIDTH_PX + 'px', maxWidth: DEFAULT_MAX_WIDTH_PX + 'px' }]) {
32
32
  let th;
33
- if (head.value == 'non-resizable' || head.value == 'slot-append') {
33
+ if (head.value == 'non-resizable' || head.value == 'customize-headers') {
34
34
  th = document.getElementsByClassName(head.value).item(0);
35
35
  }
36
36
  else {
@@ -40,14 +40,20 @@ onMount(() => {
40
40
  resizeHeader(th, head);
41
41
  }
42
42
  }
43
- let table = document.getElementsByClassName('table')[0];
44
- table.classList.add('resizable');
43
+ let table = document.getElementsByClassName('dynamic-table')[0];
44
+ table.classList.add('dynamic-resizable');
45
+ resizeObserver = new ResizeObserver(() => {
46
+ updateRemainingWidth();
47
+ });
48
+ if (tableContainer) {
49
+ resizeObserver.observe(tableContainer);
50
+ }
45
51
  return () => {
46
52
  window.removeEventListener('resize', updateHeaderHeight);
47
53
  tableContainer?.removeEventListener("scroll", setReachedBottomOrTop);
54
+ resizeObserver?.disconnect();
48
55
  };
49
56
  });
50
- let mainHeader = $state();
51
57
  function updateHeaderHeight() {
52
58
  if (mainHeader) {
53
59
  const headerHeight = mainHeader.getBoundingClientRect().height;
@@ -94,8 +100,8 @@ const [send, receive] = crossfade({
94
100
  };
95
101
  },
96
102
  });
97
- let { headers = [], headersToShowInTable = headers, subHeaders = [], customizeHeaders = false, rows = [], sortedBy = $bindable(), sortDirection = $bindable("asc"), cellEdit = false, lang = "en", dateLocale, noItemsText = lang == 'en' ? "No items to show" : 'Nessun elemento da visualizzare', showSelect = false, showActions = true, selectMode = "single", selectedItems = $bindable([]), unselectedItems = $bindable([]), selectedAll = $bindable(false), showExpand = false, loading = false, disabled = false, filters = $bindable([]), searchBarColumns, searchBarVisible = false, searchBarPlaceholder = lang == 'en' ? "Type to search..." : "Scrivi per cercare...", filtersVisible = false, quickFiltersVisible = false, editFilterMode = "one-edit", showActiveFilters = true, quickFilters = [], actionsForSelectedItems = [], quickActionsDisabled = false, totalRows = rows.length, searchText = $bindable(), renderedRowsNumber = 100, sectionRowsNumber = 20, sectionThreshold = 2, backwardThresholdPixel = 100, forwardThresholdPixel = 100, uniqueKey = 'id', numberOfResultsVisible = false, endLineVisible = false, resizableColumns = false, resizedColumnSizeWithPadding = {}, class: clazz = {}, onapplyCustomQuickFilter, oncellClick, onfetchData, onfiltersChange, onsort, onremoveAllFilters, onremoveCustomQuickFilter, onremoveFilter, onrowClick, onsaveCellEdit, onsaveHeadersToShow, oncolumnResize, searchBarSnippet, customFilterChipSnippet, customFilterSnippet, filterAppendSnippet, onscroll, selectionSnippet: selectionInternalSnippet, itemLabelSnippet: itemLabelInternalSnippet, chipLabelSnippet, headerSnippet, headerLabelSnippet, rowAppendSnippet, rowActionsSnippet, customRowSnippet, subRowAppendSnippet, subHeaderLabelSnippet, subHeaderSnippet, subRowActionsSnippet, customSubRowSnippet, customQuickFilterSnippet, appendSnippet, } = $props();
98
- let openCellEditor = $state(false), cellEditorActivator = $state(), cellEditorContainer = $state(), menuElementCellEditor = $state(), menuElementQuickFilters = $state(), cellEditorInfoActive = $state(), saveEditDisabled = $state(false), searchBarInput = $state(undefined), openQuickFilter = $state(false), quickFilterActivator = $state(), quickFilterActive = $state(), globalBuilder = new FilterBuilder(), calendarOpened = $state(false), calendarOpened2 = $state(false), selectedIndexes = [], cellEditorIndexRow = $state(), cellEditorIndexHeader = $state(), cellEditorSubItem = $state(), currentSectionNumber = $state(0), tableBody = $state(), tableContainer = $state(), userScrolling = $state(true), reachedBottom = $state(false), reachedTop = false, resizing = false, remainingWidth = $state(0), hideScrollbar = $state(false), sortModify;
103
+ let { headers = [], headersToShowInTable = headers, subHeaders = [], customizeHeaders = false, rows = [], sortedBy = $bindable(), sortDirection = $bindable("asc"), cellEdit = false, lang = "en", dateLocale, noItemsText = lang == 'en' ? "No items to show" : 'Nessun elemento da visualizzare', showSelect = false, showActions = true, selectMode = "single", selectedItems = $bindable([]), unselectedItems = $bindable([]), selectedAll = $bindable(false), showExpand = false, loading = false, disabled = false, filters = $bindable([]), searchBarColumns, searchBarVisible = false, searchBarPlaceholder = lang == 'en' ? "Type to search..." : "Scrivi per cercare...", filtersVisible = false, quickFiltersVisible = false, editFilterMode = "one-edit", showActiveFilters = true, quickFilters = [], actionsForSelectedItems = [], quickActionsDisabled = false, totalRows = rows.length, searchText = $bindable(), renderedRowsNumber = 100, sectionRowsNumber = 20, sectionThreshold = 2, backwardThresholdPixel = 100, forwardThresholdPixel = 100, uniqueKey = 'id', numberOfResultsVisible = false, endLineVisible = false, resizableColumns = false, resizedColumnSizeWithPadding = {}, dynamicFilters = true, class: clazz = {}, onapplyCustomQuickFilter, oncellClick, onfetchData, onfiltersChange, onsort, onremoveAllFilters, onremoveCustomQuickFilter, onremoveFilter, onrowClick, onsaveCellEdit, onsaveHeadersToShow, oncolumnResize, searchBarSnippet, customFilterChipSnippet, customFilterSnippet, filterAppendSnippet, onscroll, selectionSnippet: selectionInternalSnippet, itemLabelSnippet: itemLabelInternalSnippet, chipLabelSnippet, headerSnippet, headerLabelSnippet, rowAppendSnippet, customRowSnippet, subRowAppendSnippet, subHeaderLabelSnippet, subHeaderSnippet, subRowActionsSnippet, customSubRowSnippet, customQuickFilterSnippet, appendSnippet, } = $props();
104
+ let openCellEditor = $state(false), cellEditorActivator = $state(), cellEditorContainer = $state(), menuElementCellEditor = $state(), menuElementQuickFilters = $state(), cellEditorInfoActive = $state(), saveEditDisabled = $state(false), searchBarInput = $state(undefined), openQuickFilter = $state(false), quickFilterActivator = $state(), quickFilterActive = $state(), globalBuilder = new FilterBuilder(), calendarOpened = $state(false), calendarOpened2 = $state(false), selectedIndexes = [], cellEditorIndexRow = $state(), cellEditorIndexHeader = $state(), cellEditorSubItem = $state(), currentSectionNumber = $state(0), tableBody = $state(), tableContainer = $state(), userScrolling = $state(true), reachedBottom = $state(false), reachedTop = false, resizing = false, remainingWidth = $state(0), hideScrollbar = $state(false), sortModify, mainHeader = $state(), resizeObserver;
99
105
  const DEFAULT_MIN_WIDTH_PX = 100, DEFAULT_MAX_WIDTH_PX = 400;
100
106
  let totalSections = $derived((totalRows - renderedRowsNumber) / sectionRowsNumber);
101
107
  let hasMoreToRender = $derived(totalSections > currentSectionNumber);
@@ -835,9 +841,7 @@ function resize(node) {
835
841
  }
836
842
  }
837
843
  $effect(() => {
838
- if (resizableColumns &&
839
- !!tableContainer &&
840
- resizableColumns &&
844
+ if (!!tableContainer &&
841
845
  headersToShowInTable.length > 0 &&
842
846
  resizedColumnSizeWithPadding &&
843
847
  headersToShow.length > 0 &&
@@ -929,37 +933,57 @@ function resizeHeader(th, header) {
929
933
 
930
934
  {#if filtersVisible}
931
935
  <div>
932
- <Filters
933
- bind:filters
934
- onapplyFilter={() => {
935
- handleSearchChange(searchText);
936
- }}
937
- onremoveFilter={e => { handleRemoveFilter(e.detail.filter) }}
938
- onremoveAllFilters={() => handleRemoveAllFilters()}
939
- --filters-default-wrapper-width="100%"
940
- {lang}
941
- {dateLocale}
942
- {editFilterMode}
943
- {showActiveFilters}
944
- appendSnippet={filterAppendSnippet}
945
- customChipSnippet={customFilterChipSnippet}
946
- >
947
- {#snippet contentSnippet({ filters, mAndDown, updateMultiFilterValues, })}
948
- {#key filters}
949
- <DynamicFilters
950
- {lang}
951
- {filters}
952
- {mAndDown}
953
- onchange={e => updateFilterValues(e.detail.filter, updateMultiFilterValues)}
954
- {updateMultiFilterValues}
955
- >
956
- {#snippet customSnippet({ filter, mAndDown, updateCustomFilterValues })}
957
- {@render customFilterSnippet?.({ filter, mAndDown, updateCustomFilterValues })}
958
- {/snippet}
959
- </DynamicFilters>
960
- {/key}
961
- {/snippet}
962
- </Filters>
936
+ {#if dynamicFilters}
937
+ <Filters
938
+ bind:filters
939
+ onapplyFilter={() => {
940
+ handleSearchChange(searchText);
941
+ }}
942
+ onremoveFilter={e => { handleRemoveFilter(e.detail.filter) }}
943
+ onremoveAllFilters={() => handleRemoveAllFilters()}
944
+ --filters-default-wrapper-width="100%"
945
+ {lang}
946
+ {dateLocale}
947
+ {editFilterMode}
948
+ {showActiveFilters}
949
+ appendSnippet={filterAppendSnippet}
950
+ customChipSnippet={customFilterChipSnippet}
951
+ >
952
+ {#snippet contentSnippet({ filters, mAndDown, updateMultiFilterValues, })}
953
+ {#key filters}
954
+ <DynamicFilters
955
+ {lang}
956
+ {filters}
957
+ {mAndDown}
958
+ onchange={e => updateFilterValues(e.detail.filter, updateMultiFilterValues)}
959
+ {updateMultiFilterValues}
960
+ >
961
+ {#snippet customSnippet({ filter, mAndDown, updateCustomFilterValues })}
962
+ {@render customFilterSnippet?.({ filter, mAndDown, updateCustomFilterValues })}
963
+ {/snippet}
964
+ </DynamicFilters>
965
+ {/key}
966
+ {/snippet}
967
+ </Filters>
968
+ {:else}
969
+ <Filters
970
+ bind:filters
971
+ onapplyFilter={() => {
972
+ handleSearchChange(searchText);
973
+ }}
974
+ onremoveFilter={e => { handleRemoveFilter(e.detail.filter) }}
975
+ onremoveAllFilters={() => handleRemoveAllFilters()}
976
+ --filters-default-wrapper-width="100%"
977
+ {lang}
978
+ {dateLocale}
979
+ {editFilterMode}
980
+ {showActiveFilters}
981
+ appendSnippet={filterAppendSnippet}
982
+ customChipSnippet={customFilterChipSnippet}
983
+ customSnippet={customFilterSnippet}
984
+ >
985
+ </Filters>
986
+ {/if}
963
987
  </div>
964
988
  {/if}
965
989
  </div>
@@ -1027,7 +1051,7 @@ function resizeHeader(th, header) {
1027
1051
  hasMore={currentSectionNumber > 0 && userScrolling}
1028
1052
  direction='backward'
1029
1053
  />
1030
- <table style="display: table;" class="table">
1054
+ <table style="display: table;" class="dynamic-table">
1031
1055
  <thead class="table-header" bind:this={mainHeader}>
1032
1056
  <tr>
1033
1057
  {#if !!showSelect && !showExpand && rows.length > 0}
@@ -1114,33 +1138,28 @@ function resizeHeader(th, header) {
1114
1138
  {/if}
1115
1139
  </th>
1116
1140
  {/each}
1117
- {#if rowActionsSnippet || rowAppendSnippet}
1118
- <th
1119
- class="slot-append"
1120
- >
1121
- {@render rowAppendSnippet?.({ index: -1, row: undefined })}
1122
- </th>
1123
- {/if}
1124
- {#if resizableColumns && remainingWidth}
1141
+ {#if remainingWidth && (customizeHeaders || resizableColumns)}
1125
1142
  <th
1126
1143
  style:width={remainingWidth + 'px'}
1127
1144
  class="filler"
1128
1145
  aria-hidden="true"
1129
1146
  ></th>
1130
1147
  {/if}
1131
- {#if customizeHeaders}
1148
+ {#if customizeHeaders || rowAppendSnippet}
1132
1149
  <th
1133
- style:width="15px"
1134
- style:min-width="15px"
1135
1150
  style:text-align="center"
1136
1151
  class="customize-headers"
1137
1152
  >
1138
- <div style="display: flex; justify-content: center;">
1139
- <Icon
1140
- name="mdi-plus-circle-outline"
1141
- onclick={() => (openHeaderDrawer = true)}
1142
- />
1143
- </div>
1153
+ {#if customizeHeaders}
1154
+ <div style="display: flex; justify-content: center;">
1155
+ <Icon
1156
+ name="mdi-plus-circle-outline"
1157
+ onclick={() => (openHeaderDrawer = true)}
1158
+ />
1159
+ </div>
1160
+ {:else}
1161
+ {@render rowAppendSnippet?.({ index: -1, row: undefined })}
1162
+ {/if}
1144
1163
  </th>
1145
1164
  {/if}
1146
1165
  </tr>
@@ -1272,9 +1291,11 @@ function resizeHeader(th, header) {
1272
1291
  {/if}
1273
1292
  </td>
1274
1293
  {/each}
1275
- {#if rowActionsSnippet || rowAppendSnippet}
1294
+ {#if remainingWidth && (customizeHeaders || resizableColumns)}
1295
+ <td></td>
1296
+ {/if}
1297
+ {#if rowAppendSnippet}
1276
1298
  <td class={clazz.cell || ""}>
1277
- {@render rowActionsSnippet?.({ index: indexRow, row })}
1278
1299
  {@render rowAppendSnippet?.({ index: indexRow, row })}
1279
1300
  </td>
1280
1301
  {/if}
@@ -1866,7 +1887,7 @@ function resizeHeader(th, header) {
1866
1887
  margin-right: -15px;
1867
1888
  }
1868
1889
 
1869
- .table {
1890
+ .dynamic-table {
1870
1891
  background-color: var(
1871
1892
  --dynamic-table-background-color,
1872
1893
  var(--dynamic-table-default-background-color)
@@ -1874,7 +1895,7 @@ function resizeHeader(th, header) {
1874
1895
  border-collapse: separate;
1875
1896
  }
1876
1897
 
1877
- .table.resizable {
1898
+ .dynamic-table.dynamic-resizable {
1878
1899
  table-layout: fixed;
1879
1900
  width: fit-content;
1880
1901
  }
@@ -1993,17 +2014,17 @@ function resizeHeader(th, header) {
1993
2014
  padding-left: 10px;
1994
2015
  border: 1px solid transparent;
1995
2016
  }
1996
- table.table > tbody > tr > td {
2017
+ table.dynamic-table > tbody > tr > td {
1997
2018
  overflow: hidden;
1998
2019
  text-overflow: ellipsis;
1999
2020
  }
2000
2021
 
2001
- table.table > thead > tr > th {
2022
+ table.dynamic-table > thead > tr > th {
2002
2023
  overflow: hidden;
2003
2024
  text-overflow: ellipsis;
2004
2025
  }
2005
2026
 
2006
- table.table > tbody > tr > td.expanded-row {
2027
+ table.dynamic-table > tbody > tr > td.expanded-row {
2007
2028
  overflow: visible;
2008
2029
  }
2009
2030
 
@@ -168,6 +168,7 @@ declare class __sveltets_Render<Item extends {
168
168
  resizedColumnSizeWithPadding?: {
169
169
  [value: string]: number;
170
170
  } | undefined;
171
+ dynamicFilters?: boolean;
171
172
  class?: {
172
173
  container?: string;
173
174
  header?: string;
@@ -246,7 +247,8 @@ declare class __sveltets_Render<Item extends {
246
247
  customFilterSnippet?: Snippet<[{
247
248
  filter: Filter | undefined;
248
249
  mAndDown: boolean;
249
- updateCustomFilterValues: Parameters<NonNullable<ComponentProps<typeof Filters>["contentSnippet"]>>[0]["updateMultiFilterValues"];
250
+ updateCustomFilterValues?: Parameters<NonNullable<ComponentProps<typeof Filters>["contentSnippet"]>>[0]["updateMultiFilterValues"];
251
+ updateFunction?: Parameters<NonNullable<ComponentProps<typeof Filters>["customSnippet"]>>[0]["updateFunction"];
250
252
  }]> | undefined;
251
253
  onscroll?: UIEventHandler<HTMLDivElement>;
252
254
  selectionSnippet?: ComponentProps<typeof Autocomplete>["selectionSnippet"];
@@ -337,19 +339,6 @@ declare class __sveltets_Render<Item extends {
337
339
  })[];
338
340
  } | undefined;
339
341
  }]> | undefined;
340
- rowActionsSnippet?: Snippet<[{
341
- index: number;
342
- row?: {
343
- item: Item & {
344
- disableEdit?: boolean;
345
- rowDisableBackgroundColor?: string;
346
- };
347
- subItems: (Item & {
348
- disableEdit?: boolean;
349
- rowDisableBackgroundColor?: string;
350
- })[];
351
- } | undefined;
352
- }]> | undefined;
353
342
  customRowSnippet?: Snippet<[{
354
343
  index: number;
355
344
  columnIndex: number;
@@ -4,13 +4,13 @@
4
4
  <script lang="ts" generics="Item extends {[key: string]: any}, Data">import '../../../css/main.css';
5
5
  import './SimpleTable.css';
6
6
  import Icon from '../media/Icon.svelte';
7
- import { onMount } from 'svelte';
7
+ import { onMount, tick } from 'svelte';
8
8
  import NoData from '../common/NoData.svelte';
9
9
  let { headers = [], items = [], sortedBy = $bindable(undefined), sortDirection = $bindable("asc"), resizableColumns = false, resizedColumnSizeWithPadding = $bindable(), pointerOnRowHover = false, lang = 'en', doubleClickActive = false, doubleClickDelay = 250, calculateRowStyles = undefined, calculateRowClasses = undefined, oncolumnResize, onrowClick, onrowDoubleClick, onsort, headerSnippet, headerLabelSnippet, appendSnippet, rowActionsSnippet, customSnippet, noDataSnippet, class: clazz = {}, } = $props();
10
10
  if (!onrowClick && !!onrowDoubleClick) {
11
11
  throw new Error('cannot define an onrowDoubleClick event without defining an onrowClick event');
12
12
  }
13
- let clickTimeout = undefined, sortModify;
13
+ let clickTimeout = undefined, sortModify, tableContainer = $state(), mainHeader = $state(), remainingWidth = $state(0), resizeObserver;
14
14
  onMount(() => {
15
15
  if (resizableColumns) {
16
16
  if (!resizedColumnSizeWithPadding)
@@ -50,6 +50,15 @@ onMount(() => {
50
50
  }
51
51
  let table = document.getElementsByClassName('table')[0];
52
52
  table.classList.add('resizable');
53
+ resizeObserver = new ResizeObserver(() => {
54
+ updateRemainingWidth();
55
+ });
56
+ if (tableContainer) {
57
+ resizeObserver.observe(tableContainer);
58
+ }
59
+ return () => {
60
+ resizeObserver?.disconnect();
61
+ };
53
62
  }
54
63
  });
55
64
  function handleHeaderClick(header) {
@@ -176,12 +185,51 @@ function resize(node) {
176
185
  };
177
186
  }
178
187
  }
188
+ $effect(() => {
189
+ if (resizableColumns &&
190
+ !!tableContainer &&
191
+ resizableColumns &&
192
+ headers.length > 0 &&
193
+ resizedColumnSizeWithPadding &&
194
+ mainHeader) {
195
+ tick().then(updateRemainingWidth);
196
+ }
197
+ });
198
+ async function updateRemainingWidth() {
199
+ if (tableContainer != null && !!tableContainer && mainHeader) {
200
+ const containerWidth = tableContainer?.getBoundingClientRect().width - 30;
201
+ if (containerWidth) {
202
+ const totalResizableWidth = headers.reduce((sum, head) => {
203
+ let th = document.getElementById(head.value);
204
+ if (!!th) {
205
+ resizeHeader(th, head);
206
+ }
207
+ const width = th?.getBoundingClientRect().width || 0;
208
+ return sum + width + 1;
209
+ }, 0);
210
+ const extraStaticWidth = Array.from(mainHeader.querySelectorAll('th.non-resizable, th.slot-append, th.customize-headers'))
211
+ .reduce((sum, th) => sum + th.getBoundingClientRect().width + 1, 0);
212
+ remainingWidth = Math.max(0, containerWidth - totalResizableWidth - extraStaticWidth);
213
+ }
214
+ }
215
+ }
216
+ function resizeHeader(th, header) {
217
+ if (resizedColumnSizeWithPadding) {
218
+ if (!resizedColumnSizeWithPadding[header.value]) {
219
+ let widthWihtPadding = th.getBoundingClientRect().width;
220
+ resizedColumnSizeWithPadding[header.value] = widthWihtPadding;
221
+ }
222
+ let { paddingLeft, paddingRight } = getComputedStyle(th);
223
+ let width = resizedColumnSizeWithPadding[header.value] - parseFloat(paddingLeft) - parseFloat(paddingRight);
224
+ th.style.width = `${width}px`;
225
+ }
226
+ }
179
227
  </script>
180
228
 
181
229
  {#if !!items && Array.isArray(items)}
182
- <div class="simple-table-container {clazz.container || ''}" class:resizable={resizableColumns}>
230
+ <div class="simple-table-container {clazz.container || ''}" class:resizable={resizableColumns} bind:this={tableContainer}>
183
231
  <table class="table">
184
- <thead class="thead {clazz.header || ''}">
232
+ <thead class="thead {clazz.header || ''}" bind:this={mainHeader}>
185
233
  <tr>
186
234
  {#each headers as head}
187
235
  <th
@@ -227,6 +275,13 @@ function resize(node) {
227
275
  {/if}
228
276
  </th>
229
277
  {/each}
278
+ {#if resizableColumns && remainingWidth}
279
+ <th
280
+ style:width={remainingWidth + 'px'}
281
+ class="filler"
282
+ aria-hidden="true"
283
+ ></th>
284
+ {/if}
230
285
  {#if rowActionsSnippet || appendSnippet}
231
286
  <th class="slot-append">
232
287
  {#if appendSnippet}
@@ -286,6 +341,9 @@ function resize(node) {
286
341
  {/if}
287
342
  </td>
288
343
  {/each}
344
+ {#if resizableColumns && remainingWidth}
345
+ <td></td>
346
+ {/if}
289
347
  {#if rowActionsSnippet || appendSnippet}
290
348
  <td class="{clazz.cell || ''} append" style:width="fit-content">
291
349
  {#if rowActionsSnippet}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@likable-hair/svelte",
3
3
  "description": "A Svelte component for likablehair and others",
4
- "version": "4.0.7",
4
+ "version": "4.0.8",
5
5
  "scripts": {
6
6
  "host": "vite --host",
7
7
  "dev": "vite dev",