@likable-hair/svelte 4.2.3 → 4.2.5

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.
@@ -1,14 +1,14 @@
1
1
  :root {
2
- --quick-actions-default-background-color: rgb(var(--global-color-background-200));
2
+ --quick-actions-default-background-color: rgb(var(--global-color-background-300));
3
3
  --quick-actions-default-selected-items-button-background-color: rgb(var(--global-color-background-500));
4
- --quick-actions-default-selected-items-button-background-color-hover: rgb(var(--global-color-background-300));
5
- --quick-actions-default-selected-items-button-background-color-disabled: rgb(var(--global-color-background-300), .5);
4
+ --quick-actions-default-selected-items-button-background-color-hover: rgb(var(--global-color-background-400));
5
+ --quick-actions-default-selected-items-button-background-color-disabled: rgb(var(--global-color-background-400), .5);
6
6
  --quick-actions-default-selected-items-button-color-disabled: rgb(var(--global-color-contrast-900), .5);
7
7
  --quick-actions-default-z-index: 48;
8
8
 
9
9
  --quick-actions-default-buttons-background-color: var(--quick-actions-background-color, var(--quick-actions-default-background-color));
10
- --quick-actions-default-buttons-background-color-disabled: rgb(var(--global-color-background-300), .5);
11
- --quick-actions-default-buttons-background-color-hover: rgb(var(--global-color-background-300));
10
+ --quick-actions-default-buttons-background-color-disabled: rgb(var(--global-color-background-400), .5);
11
+ --quick-actions-default-buttons-background-color-hover: rgb(var(--global-color-background-400));
12
12
  --quick-actions-default-buttons-color: rgb(var(--global-color-contrast-900));
13
13
  --quick-actions-default-buttons-color-disabled: rgb(var(--global-color-contrast-900), .5)
14
14
  }
@@ -98,7 +98,11 @@ $effect(() => {
98
98
  --circular-loader-height="17px"
99
99
  disabled={action.disabled || action.loading || disabled}
100
100
  loading={action.loading}
101
- onclick={action.onClick}
101
+ onclick={e => {
102
+ action.loading = true
103
+ action.onClick(e)
104
+ action.loading = false
105
+ }}
102
106
  >
103
107
  <div class="action" bind:this={disabledInfoActivators[action.label]}>
104
108
  {#if action.icon}
@@ -130,8 +134,7 @@ $effect(() => {
130
134
  --button-hover-color: var(--quick-actions-buttons-color, var(--quick-actions-default-buttons-color));
131
135
  --button-focus-color: var(--quick-actions-buttons-color, var(--quick-actions-default-buttons-color));
132
136
  --button-disabled-color: var(--quick-actions-buttons-color-disabled, var(--quick-actions-default-buttons-color-disabled));
133
- --button-box-shadow: none;"
134
- margin-left: 8px;
137
+ --button-box-shadow: none;
135
138
  '
136
139
  --button-height="20px"
137
140
  onclick={(e) => {
@@ -189,7 +192,11 @@ $effect(() => {
189
192
  --circular-loader-height="25px"
190
193
  disabled={action.disabled || action.loading || disabled}
191
194
  loading={action.loading}
192
- onclick={action.onClick}
195
+ onclick={e => {
196
+ action.loading = true
197
+ action.onClick(e)
198
+ action.loading = false
199
+ }}
193
200
  >
194
201
  <div class="action" bind:this={disabledInfoActivators[action.label]}>
195
202
  {#if action.icon}
@@ -350,13 +357,13 @@ $effect(() => {
350
357
  align-items: center;
351
358
  box-shadow: 2px 2px 2px 2px rgba(0, 0, 0, 0.2);
352
359
  position: fixed;
353
- padding: 10px;
354
- border-radius: 10px;
360
+ padding: 8px;
361
+ border-radius: 8px;
355
362
  background-color: var(
356
363
  --quick-actions-background-color,
357
364
  var(--quick-actions-default-background-color)
358
365
  );
359
- gap: 15px;
366
+ gap: 4px;
360
367
  left: 79dvw;
361
368
  }
362
369
 
@@ -381,13 +388,13 @@ $effect(() => {
381
388
  align-items: center;
382
389
  box-shadow: 2px 2px 2px 2px rgba(0, 0, 0, 0.2);
383
390
  position: fixed;
384
- padding: 10px;
385
- border-radius: 10px;
391
+ padding: 8px;
392
+ border-radius: 8px;
386
393
  background-color: var(
387
394
  --quick-actions-background-color,
388
395
  var(--quick-actions-default-background-color)
389
396
  );
390
- gap: 15px;
397
+ gap: 4px;
391
398
  left: 73dvw;
392
399
  }
393
400
  }
@@ -50,10 +50,12 @@ declare class __sveltets_Render<Data> {
50
50
  menuFlipOnOverflow?: ComponentProps<typeof Menu>["flipOnOverflow"];
51
51
  menuMaxHeight?: string;
52
52
  adaptInputWidth?: boolean;
53
+ hint?: string;
53
54
  class?: {
54
55
  activator?: string;
55
56
  menu?: string;
56
57
  simpleTextfield?: ComponentProps<typeof import("../../..").SimpleTextField>["class"];
58
+ hint?: string;
57
59
  } | undefined;
58
60
  selectionContainerSnippet?: import("svelte").Snippet<[{
59
61
  values: Item<Data>[];
@@ -87,6 +89,9 @@ declare class __sveltets_Render<Data> {
87
89
  index: number;
88
90
  selected: boolean;
89
91
  }]> | undefined;
92
+ hintSnippet?: import("svelte").Snippet<[{
93
+ hint?: string;
94
+ }]> | undefined;
90
95
  onchange?: ((event: {
91
96
  detail: {
92
97
  unselect: Item<Data> | undefined;
@@ -52,7 +52,7 @@ function handleCloseClick(event) {
52
52
  event.detail.nativeEvent.preventDefault()
53
53
  }
54
54
  }}
55
- --button-padding="var(--dropdown-button-padding, 10px 12px)"
55
+ --button-padding="var(--dropdown-button-padding, 6px 8px)"
56
56
  --button-border='var(--dropdown-button-border, none)'
57
57
  --button-background-color='var(--dropdown-button-background-color, rgb(var(--global-color-background-300), .6))'
58
58
  --button-hover-background-color='var(--dropdown-button-hover-color, rgb(var(--global-color-background-300), .6))'
@@ -22,22 +22,7 @@ const deepEqual = lodash.isEqual;
22
22
  onMount(() => {
23
23
  (async () => {
24
24
  await tick();
25
- if (rowAppendSnippet && headersHTML['row-append-header']) {
26
- const actionCells = tableContainer?.querySelectorAll('.row-append-cell');
27
- if (actionCells && actionCells.length > 0) {
28
- let maxActionWidth = 0;
29
- for (let i = 0; i < actionCells.length; i++) {
30
- const cellContent = actionCells[i];
31
- const width = cellContent.getBoundingClientRect().width;
32
- if (width > maxActionWidth) {
33
- maxActionWidth = width;
34
- }
35
- }
36
- const finalWidth = Math.max(Math.ceil(maxActionWidth), 40);
37
- headersHTML['row-append-header'].style.width = `${finalWidth}px`;
38
- headersHTML['row-append-header'].style.minWidth = `${finalWidth}px`;
39
- }
40
- }
25
+ resizeRowAppendHeader();
41
26
  updateHeaderHeight();
42
27
  window.addEventListener('resize', updateHeaderHeight);
43
28
  tableContainer?.addEventListener("scroll", setReachedBottomOrTop);
@@ -50,7 +35,6 @@ onMount(() => {
50
35
  resizeHeader(th, head);
51
36
  }
52
37
  }
53
- tableHTML?.classList.add('dynamic-resizable');
54
38
  resizeObserver = new ResizeObserver(() => {
55
39
  if (tableContainer) {
56
40
  const rect = tableContainer.getBoundingClientRect();
@@ -161,7 +145,7 @@ function calculateStickyMetrics() {
161
145
  }
162
146
  totalStickyWidth = width;
163
147
  }
164
- const DEFAULT_MIN_WIDTH_PX = 130, DEFAULT_MAX_WIDTH_PX = 400;
148
+ const DEFAULT_MIN_WIDTH_PX = 60, DEFAULT_MAX_WIDTH_PX = 400;
165
149
  let totalSections = $derived((totalRows - renderedRowsNumber) / sectionRowsNumber);
166
150
  let hasMoreToRender = $derived(totalSections > currentSectionNumber);
167
151
  let totalCachedSections = $derived((rows.length - renderedRowsNumber) / sectionRowsNumber);
@@ -903,7 +887,7 @@ $effect(() => {
903
887
  });
904
888
  async function updateRemainingWidth() {
905
889
  if (tableContainer != null && !!tableContainer && mainHeader) {
906
- const containerWidth = tableContainer.getBoundingClientRect().width - 10;
890
+ const containerWidth = tableContainer.getBoundingClientRect().width;
907
891
  if (containerWidth) {
908
892
  const totalResizableWidth = headersToShowInTable.reduce((sum, head) => {
909
893
  let th = headersHTML[head.value];
@@ -911,17 +895,43 @@ async function updateRemainingWidth() {
911
895
  resizeHeader(th, head);
912
896
  }
913
897
  const width = th?.getBoundingClientRect().width || 0;
914
- return sum + width + 1;
898
+ return sum + width;
915
899
  }, 0);
900
+ resizeRowAppendHeader();
916
901
  const extraStaticWidth = Array.from(mainHeader.querySelectorAll('th.non-resizable, th.row-append-header'))
917
- .reduce((sum, th) => sum + th.getBoundingClientRect().width + 1, 0);
918
- remainingWidth = Math.max(0, containerWidth - totalResizableWidth - extraStaticWidth + 18);
902
+ .reduce((sum, th) => sum + th.getBoundingClientRect().width, 0);
903
+ remainingWidth = Math.max(0, containerWidth - totalResizableWidth - extraStaticWidth);
919
904
  }
920
905
  }
921
906
  }
907
+ function resizeRowAppendHeader() {
908
+ if ((customizeHeaders || rowAppendSnippet) && headersHTML['row-append-header']) {
909
+ if (!!headersHTML['row-append-header'].style.width && headersHTML['row-append-header'].style.width != "0px") {
910
+ return;
911
+ }
912
+ if (tableHTML) {
913
+ tableHTML.style.tableLayout = 'auto';
914
+ }
915
+ let widthWithPadding = headersHTML['row-append-header'].scrollWidth;
916
+ if (tableHTML) {
917
+ tableHTML.style.tableLayout = 'fixed';
918
+ }
919
+ headersHTML['row-append-header'].style.width = `${widthWithPadding}px`;
920
+ headersHTML['row-append-header'].style.minWidth = `${widthWithPadding}px`;
921
+ }
922
+ }
922
923
  function resizeHeader(th, header) {
923
924
  if (!resizedColumnSizeWithPadding[header.value]) {
924
- let widthWithPadding = th.getBoundingClientRect().width;
925
+ if (tableHTML) {
926
+ tableHTML.style.tableLayout = 'auto';
927
+ }
928
+ let widthWithPadding = th.scrollWidth;
929
+ if (widthWithPadding == 0) {
930
+ return;
931
+ }
932
+ if (tableHTML) {
933
+ tableHTML.style.tableLayout = 'fixed';
934
+ }
925
935
  let minWidth = header.minWidth, minWidthPx = DEFAULT_MIN_WIDTH_PX;
926
936
  if (!!minWidth && minWidth.endsWith('px')) {
927
937
  minWidthPx = parseInt(minWidth, 10);
@@ -1139,7 +1149,7 @@ function handleSaveHeadersToShow(event) {
1139
1149
  hasMore={currentSectionNumber > 0 && userScrolling}
1140
1150
  direction='backward'
1141
1151
  />
1142
- <table style="display: table;" class="dynamic-table" bind:this={tableHTML}>
1152
+ <table style="display: table;" class="dynamic-table dynamic-resizable" bind:this={tableHTML}>
1143
1153
  <thead class="table-header {clazz.header}" bind:this={mainHeader}>
1144
1154
  <tr>
1145
1155
  {#if !!showSelect && !showExpand && rows.length > 0}
@@ -1251,6 +1261,7 @@ function handleSaveHeadersToShow(event) {
1251
1261
  <th
1252
1262
  style:width={remainingWidth + 'px'}
1253
1263
  class="filler"
1264
+ style:padding=0
1254
1265
  aria-hidden="true"
1255
1266
  ></th>
1256
1267
  {/if}
@@ -1930,13 +1941,8 @@ function handleSaveHeadersToShow(event) {
1930
1941
  width: 100%;
1931
1942
  }
1932
1943
 
1933
- .hide-scrollbar {
1934
- -ms-overflow-style: none;
1935
- scrollbar-width: none;
1936
- }
1937
-
1938
1944
  .hide-scrollbar::-webkit-scrollbar {
1939
- display: none;
1945
+ width: 0px;
1940
1946
  }
1941
1947
 
1942
1948
  .dynamic-table {
@@ -255,10 +255,7 @@ function handleSelect(item, shiftKeyPressed) {
255
255
  {#if index == -1}
256
256
  {#if selectionMode == 'multiple' && !hideSelectAll}
257
257
  <div
258
- style:display=flex
259
- style:align-items=middle
260
- style:justify-content=center
261
- style:padding="8px 0px"
258
+ style:padding-left="8px"
262
259
  >
263
260
  <Checkbox
264
261
  id="select-all"
@@ -10,28 +10,25 @@ import ToggleList from "../forms/ToggleList.svelte";
10
10
  import { tick } from "svelte";
11
11
  let { filter = $bindable(), lang = 'en', betweenFromLabel = lang === 'en' ? "From" : "Da", betweenToLabel = lang === 'en' ? "To" : "A", labelsMapper, forceApplyValid = false, editFilterMode = 'one-edit', tmpFilter = $bindable(), mobile = false, onchange, onclick: onclickInternal, onkeypress, customSnippet, filterActionsSnippet, } = $props();
12
12
  let advancedModeOptions = $state(), advancedModeSelectedOptions = $state([]);
13
- function initTmpFilter() {
14
- tmpFilter = filter === undefined ? undefined : { ...filter };
15
- if (!!tmpFilter && ['string', 'number', 'date', 'select'].includes(tmpFilter.type) && Object.keys(tmpFilter).includes('mode')) {
16
- //@ts-ignore
17
- if ((tmpFilter.mode == 'between' && tmpFilter.from !== undefined && tmpFilter.to !== undefined) || tmpFilter.value !== undefined || (tmpFilter.type == 'select' && tmpFilter.values !== undefined && tmpFilter.values.length > 0)) {
18
- advancedModeSelectedOptions = [{
19
- //@ts-ignore
20
- value: tmpFilter.mode, label: labelsMapper[tmpFilter.mode].short || tmpFilter.mode
21
- }];
22
- }
23
- }
24
- }
25
13
  function closeDropDown() {
26
14
  dropdownOpened = false;
27
15
  }
28
16
  $effect(() => {
29
17
  if (!!filter && !tmpFilter) {
30
18
  tick().then(() => {
31
- initTmpFilter();
19
+ tmpFilter = filter === undefined ? undefined : { ...filter };
32
20
  closeDropDown();
33
21
  });
34
22
  }
23
+ if (!!tmpFilter && ['string', 'number', 'date', 'select'].includes(tmpFilter.type) && Object.keys(tmpFilter).includes('mode')) {
24
+ //@ts-ignore
25
+ if ((tmpFilter.mode == 'between' && tmpFilter.from !== undefined && tmpFilter.to !== undefined) || tmpFilter.value !== undefined || (tmpFilter.type == 'select' && tmpFilter.values !== undefined && tmpFilter.values.length > 0)) {
26
+ advancedModeSelectedOptions = [{
27
+ //@ts-ignore
28
+ value: tmpFilter.mode, label: labelsMapper[tmpFilter.mode].short || tmpFilter.mode
29
+ }];
30
+ }
31
+ }
35
32
  });
36
33
  $effect(() => {
37
34
  if (!!tmpFilter) {
@@ -123,7 +120,7 @@ function onclick(event) {
123
120
  {#if !!filter && !!tmpFilter}
124
121
  <div class="filter-container" style:margin={editFilterMode === 'one-edit' ? '5%' : '0'}>
125
122
  <div class="filter-editor" class:row={
126
- (tmpFilter.type == 'number' || (tmpFilter.type == 'date' && ((advancedModeSelectedOptions.length && advancedModeSelectedOptions[0].value != 'between') || tmpFilter.betweenModeSingleTextField)))
123
+ (tmpFilter.type == 'number' || tmpFilter.type == 'string' || (tmpFilter.type == 'date' && ((advancedModeSelectedOptions.length && advancedModeSelectedOptions[0].value != 'between') || tmpFilter.betweenModeSingleTextField)))
127
124
  }>
128
125
  {#if filter.advanced}
129
126
  <div class="advanced-mode">
@@ -166,6 +163,7 @@ function onclick(event) {
166
163
  type="text"
167
164
  placeholder={editFilterMode == 'one-edit' ? tmpFilter?.label : undefined}
168
165
  --simple-textfield-width="100%"
166
+ --simple-textfield-padding='0.45rem 0.6rem'
169
167
  oninput={() => handleChangeValue()}
170
168
  ></SimpleTextField>
171
169
  {:else if tmpFilter.type === "date" && tmpFilter.mode !== 'between'}
@@ -175,7 +173,7 @@ function onclick(event) {
175
173
  openingId="advanced-filter"
176
174
  bind:menuOpened={calendarOpened}
177
175
  --simple-textfield-width="100%"
178
- --simple-textfield-padding='0.50rem 0.6rem'
176
+ --simple-textfield-padding='0.45rem 0.6rem'
179
177
  flipOnOverflow
180
178
  oninput={() => handleChangeValue()}
181
179
  ondayClick={() => handleChangeValue()}
@@ -199,6 +197,7 @@ function onclick(event) {
199
197
  type="number"
200
198
  placeholder={editFilterMode == 'one-edit' ? tmpFilter?.label : undefined}
201
199
  --simple-textfield-width="100%"
200
+ --simple-textfield-padding='0.45rem 0.6rem'
202
201
  onchange={() => handleChangeValue()}
203
202
  ></SimpleTextField>
204
203
  </div>
@@ -241,7 +240,7 @@ function onclick(event) {
241
240
  placeholderTo={betweenToLabel}
242
241
  bind:menuOpened={calendarOpened}
243
242
  --simple-textfield-width="100%"
244
- --simple-textfield-padding='0.50rem 0.6rem'
243
+ --simple-textfield-padding='0.45rem 0.6rem'
245
244
  oninput={() => handleChangeValue(tmpFilter?.type == 'date' && tmpFilter.mode == 'between' && (!tmpFilter.from || !tmpFilter.to))}
246
245
  ondayClick={() => handleChangeValue(tmpFilter?.type == 'date' && tmpFilter.mode == 'between' && (!tmpFilter.from || !tmpFilter.to))}
247
246
  >
@@ -266,6 +265,7 @@ function onclick(event) {
266
265
  placeholder={betweenFromLabel}
267
266
  bind:menuOpened={calendarOpened}
268
267
  --simple-textfield-width="100%"
268
+ --simple-textfield-padding='0.45rem 0.6rem'
269
269
  oninput={() => handleChangeValue()}
270
270
  ondayClick={() => handleChangeValue()}
271
271
  >
@@ -289,6 +289,7 @@ function onclick(event) {
289
289
  placeholder={betweenToLabel}
290
290
  bind:menuOpened={calendarOpened2}
291
291
  --simple-textfield-width="100%"
292
+ --simple-textfield-padding='0.45rem 0.6rem'
292
293
  flipOnOverflow
293
294
  oninput={() => handleChangeValue()}
294
295
  ondayClick={() => handleChangeValue()}
@@ -315,6 +316,7 @@ function onclick(event) {
315
316
  type="number"
316
317
  placeholder={betweenFromLabel}
317
318
  --simple-textfield-width="100%"
319
+ --simple-textfield-padding='0.45rem 0.6rem'
318
320
  onchange={() => handleChangeValue()}
319
321
  ></SimpleTextField>
320
322
  </div>
@@ -324,6 +326,7 @@ function onclick(event) {
324
326
  type="number"
325
327
  placeholder={betweenToLabel}
326
328
  --simple-textfield-width="100%"
329
+ --simple-textfield-padding='0.45rem 0.6rem'
327
330
  onchange={() => handleChangeValue()}
328
331
  ></SimpleTextField>
329
332
  </div>
@@ -22,7 +22,7 @@ import { DateTime } from 'luxon';
22
22
  import { TabSwitcher } from '../../..';
23
23
  let { filters = $bindable([]), lang = 'en', addFilterLabel = lang === 'en' ? "Filters" : "Filtri", cancelFilterLabel = lang === 'en' ? "Cancel" : "Annulla", applyFilterLabel = lang === 'en' ? "Apply filters" : "Applica filtri", showActiveFilters = true, filterTitleLabel = lang === 'en' ? "Filter by" : "Filtra per", dateLocale = lang === 'en' ? 'en' : 'it', betweenSeparator = lang === 'en' ? "and" : "e", trueString = lang === 'en' ? "true" : "vero", falseString = lang === 'en' ? "false" : "falso", editFilterMode = 'one-edit', labelsMapper = lang === 'en'
24
24
  ? ENGLISH_LABELS_MAPPER
25
- : ITALIAN_LABELS_MAPPER, drawerSpace = '60vh', multiEditTabs, onaddFilterClick, onapplyFilter, onremoveAllFilters, onremoveFilter, customSnippet: customInternalSnippet, customChipSnippet, appendSnippet, contentSnippet, onclick: onclickInternal, onkeydown, } = $props();
25
+ : ITALIAN_LABELS_MAPPER, drawerSpace = '85vh', multiEditTabs, onaddFilterClick, onapplyFilter, onremoveAllFilters, onremoveFilter, customSnippet: customInternalSnippet, customChipSnippet, appendSnippet, contentSnippet, onclick: onclickInternal, onkeydown, } = $props();
26
26
  let open = $state(false), mobileOpen = $state(false), activator = $state(), localMultiEditTabs = $derived.by(() => {
27
27
  return filters.some(f => !multiEditTabs?.find(tab => tab.name == f.tabName))
28
28
  ? undefined
@@ -462,7 +462,7 @@ function onclick(event, stopPropagation = false) {
462
462
  _borderRadius="20px 20px 0px 0px"
463
463
  _space={drawerSpace}
464
464
  >
465
- <div class="drawer-content">
465
+ <div class="drawer-content" class:shortened-drawer-content={editFilterMode === 'one-edit' && !(!!selectedFilter && singleFilterMenuOpened)}>
466
466
  {#if editFilterMode === 'one-edit'}
467
467
  {#if !!selectedFilter && singleFilterMenuOpened}
468
468
  <div
@@ -550,25 +550,34 @@ function onclick(event, stopPropagation = false) {
550
550
  style:height="100%"
551
551
  >
552
552
  <div class="form-container" style:background-color={mAndDown ? 'transparent' : 'rgb(var(--global-color-background-100))'} style:width={mAndDown ? '100%' : '50vw'} style:box-sizing="border-box">
553
- <div class="header">
553
+ <div class="header" class:no-border={localMultiEditTabs?.length}>
554
554
  <div>{addFilterLabel}</div>
555
555
  </div>
556
- <div class="drawer-body">
556
+ <TabSwitcher
557
+ tabs={localMultiEditTabs}
558
+ bind:selected={selectedTab}
559
+ ></TabSwitcher>
560
+ <div class="drawer-body" class:shortened-body-mobile={localMultiEditTabs?.length}>
557
561
  {#if contentSnippet}
558
562
  {@render contentSnippet({ mAndDown, updateMultiFilterValues, filters, handleRemoveAllFilters })}
559
563
  {:else}
560
564
  <div class="multi-filters-container" style:grid-template-columns={mAndDown ? '1fr' : '1fr 1fr'}>
561
- {#each filters as filter, i}
565
+ {#each selectedTabFilters as filter, i}
562
566
  <div class="filter">
563
567
  <div class="input">
564
- {#if !filter.advanced && filter.type !== 'custom'}
565
- <div class="label">
568
+ {#if filter.type !== 'custom'}
569
+ <div class="card-title">
570
+ {#if filter.icon}
571
+ <span style:margin-right=2px>
572
+ <Icon name={filter.icon} --icon-color={filter.iconColor} --icon-size=15.5px />
573
+ </span>
574
+ {/if}
566
575
  {filter.label}
567
576
  </div>
568
577
  {/if}
569
578
  <div class="field">
570
579
  <FilterEditor
571
- filter={filters[i]}
580
+ filter={selectedTabFilters[i]}
572
581
  {lang}
573
582
  {labelsMapper}
574
583
  editFilterMode="multi-edit"
@@ -576,7 +585,7 @@ function onclick(event, stopPropagation = false) {
576
585
  mobile={mAndDown}
577
586
  onchange={(e) => {
578
587
  if(!!e.detail.filter)
579
- filters[i] = e.detail.filter
588
+ selectedTabFilters[i] = e.detail.filter
580
589
  }}
581
590
  >
582
591
  {#snippet customSnippet({ filter })}
@@ -591,29 +600,42 @@ function onclick(event, stopPropagation = false) {
591
600
  {/if}
592
601
  </div>
593
602
  <div class="footer">
594
- <div class="actions" style:padding-bottom={mAndDown ? '20px' : undefined}>
603
+ <div class="actions">
595
604
  <Button
596
- --button-background-color="var(--filters-button-cancel-background-color, var(--filters-button-cancel-default-background-color))"
597
- --button-color="var(--filters-button-cancel-color, var(--filters-button-cancel-default-color))"
598
- --button-hover-background-color="rgb(var(--global-color-primary-500))"
599
- --button-hover-box-shadow="0 0 0.5rem rgba(0, 0, 0, 0.3)"
605
+ --button-color="rgb(var(--global-color-contrast-900))"
606
+ --button-background-color="transparent"
607
+ --button-focus-background-color="rgb(var(--global-color-background-300, .5))"
608
+ --button-active-background-color="rgb(var(--global-color-background-300, .5))"
609
+ --button-hover-background-color="rgb(var(--global-color-background-300, .5))"
610
+ --button-focus-color="rgb(var(--global-color-contrast-900))"
611
+ --button-active-color="rgb(var(--global-color-contrast-900))"
612
+ --button-hover-color="rgb(var(--global-color-contrast-900))"
613
+ --button-hover-box-shadow="none"
600
614
  --button-box-shadow="none"
615
+ --button-padding="12px 16px"
601
616
  onclick={handleCancelFilterClick}
602
617
  >
603
618
  {cancelFilterLabel}
604
619
  </Button>
605
620
  <Button
606
- --button-color="rgb(var(--global-color-primary-400))"
621
+ --button-color="rgb(var(--global-color-contrast-900))"
607
622
  --button-background-color="transparent"
608
- --button-hover-background-color="rgb(var(--global-color-primary-500))"
609
- --button-hover-box-shadow="0 0 0.5rem rgba(0, 0, 0, 0.3)"
623
+ --button-focus-background-color="rgb(var(--global-color-background-300, .5))"
624
+ --button-active-background-color="rgb(var(--global-color-background-300, .5))"
625
+ --button-hover-background-color="rgb(var(--global-color-background-300, .5))"
626
+ --button-focus-color="rgb(var(--global-color-contrast-900))"
627
+ --button-active-color="rgb(var(--global-color-contrast-900))"
628
+ --button-hover-color="rgb(var(--global-color-contrast-900))"
629
+ --button-hover-box-shadow="none"
610
630
  --button-box-shadow="none"
631
+ --button-padding="12px 16px"
611
632
  onclick={handleMultiEditRemoveClick}
612
633
  >
613
634
  {lang == 'en' ? "Remove filters" : "Rimuovi filtri"}
614
635
  </Button>
615
636
  <Button
616
- --button-min-width="100px"
637
+ --button-min-width="fit-content"
638
+ --button-padding="12px 16px"
617
639
  onclick={handleApplyMultiFilterClick}
618
640
  >
619
641
  {applyFilterLabel}
@@ -934,6 +956,10 @@ function onclick(event, stopPropagation = false) {
934
956
  height: 100%;
935
957
  }
936
958
 
959
+ .shortened-drawer-content{
960
+ height: calc(100% - 20px);
961
+ }
962
+
937
963
  .more-items {
938
964
  position: relative;
939
965
  padding: 0px 4px;
@@ -1019,12 +1045,8 @@ function onclick(event, stopPropagation = false) {
1019
1045
  width: 100%;
1020
1046
  }
1021
1047
 
1022
- .input .label {
1023
- margin-bottom: 5px;
1024
- }
1025
-
1026
1048
  .footer {
1027
- margin-top: 40px;
1049
+ margin-top: 20px;
1028
1050
  }
1029
1051
 
1030
1052
  .footer .actions {
@@ -1062,6 +1084,9 @@ function onclick(event, stopPropagation = false) {
1062
1084
 
1063
1085
  .drawer-body {
1064
1086
  padding: 16px;
1087
+ overflow-y: scroll;
1088
+ height: calc(89vh - 170px);
1089
+ max-height: calc(89vh - 170px);
1065
1090
  }
1066
1091
 
1067
1092
  .dialog-body {
@@ -1072,13 +1097,18 @@ function onclick(event, stopPropagation = false) {
1072
1097
  }
1073
1098
 
1074
1099
  .shortened-body {
1075
- height: calc(90vh - 170px);
1076
- max-height: calc(90vh - 170px);
1100
+ height: calc(90vh - 186px);
1101
+ max-height: calc(90vh - 186px);
1102
+ }
1103
+
1104
+ .shortened-body-mobile {
1105
+ height: calc(83vh - 170px);
1106
+ max-height: calc(83vh - 170px);
1077
1107
  }
1078
1108
 
1079
1109
  .dialog-footer {
1080
- height: 64px;
1081
- padding: 16px;
1110
+ height: 68px;
1111
+ padding: 12px;
1082
1112
  display: flex;
1083
1113
  align-items: center;
1084
1114
  justify-content: flex-end;
@@ -9,18 +9,79 @@ import MediaQuery from "../../simple/common/MediaQuery.svelte";
9
9
  import Drawer from "../../simple/navigation/Drawer.svelte";
10
10
  let { filters = $bindable(), buttonLabelSnippet, onapply, lang, } = $props();
11
11
  let activators = $state({}), openMenus = $state({});
12
+ function handleClick(params) {
13
+ if (filters) {
14
+ let filter = filters[params.index];
15
+ if (filter.type == 'bool') {
16
+ filter.value = !filter.value;
17
+ handleApplyFilter(params);
18
+ }
19
+ else {
20
+ openMenus[filter.name] = true;
21
+ }
22
+ }
23
+ }
12
24
  function handleApplyFilter(params) {
13
25
  if (!!filters) {
14
- filters[params.index].active = true;
26
+ filters[params.index].active = isActiveFilter(params);
15
27
  openMenus[filters[params.index].name] = false;
16
28
  onapply?.({ filter: filters[params.index], filters: filters || [] });
17
29
  }
18
30
  }
19
31
  function handleClearFilter(params) {
20
32
  if (!!filters) {
21
- filters[params.index].active = false;
22
- onapply?.({ filter: filters[params.index], filters: filters || [] });
33
+ let filter = filters[params.index];
34
+ if (filter.type == 'string' ||
35
+ filter.type == 'bool' ||
36
+ filter.type == 'choice' ||
37
+ filter.type == 'custom' ||
38
+ filter.type == 'multiString') {
39
+ filter.value = undefined;
40
+ }
41
+ else if (filter.type == 'select') {
42
+ filter.values = undefined;
43
+ }
44
+ else if (filter.type == 'number' ||
45
+ filter.type == 'date') {
46
+ if (filter.mode == 'between') {
47
+ filter.from = undefined;
48
+ filter.to = undefined;
49
+ }
50
+ else if (filter.mode == 'equal' ||
51
+ filter.mode == 'greater' ||
52
+ filter.mode == 'lower') {
53
+ filter.value = undefined;
54
+ }
55
+ }
56
+ handleApplyFilter(params);
57
+ }
58
+ }
59
+ function isActiveFilter(params) {
60
+ if (filters) {
61
+ let filter = filters[params.index];
62
+ if (filter.type == 'string' ||
63
+ filter.type == 'bool' ||
64
+ filter.type == 'choice' ||
65
+ filter.type == 'custom' ||
66
+ filter.type == 'multiString') {
67
+ return !!filter.value && filter.value != undefined;
68
+ }
69
+ else if (filter.type == 'select') {
70
+ return !!filter.values?.length;
71
+ }
72
+ else if (filter.type == 'number' ||
73
+ filter.type == 'date') {
74
+ if (filter.mode == 'between') {
75
+ return filter.from != undefined || filter.to != undefined;
76
+ }
77
+ else if (filter.mode == 'equal' ||
78
+ filter.mode == 'greater' ||
79
+ filter.mode == 'lower') {
80
+ return filter.value != undefined;
81
+ }
82
+ }
23
83
  }
84
+ return false;
24
85
  }
25
86
  </script>
26
87
 
@@ -29,9 +90,9 @@ function handleClearFilter(params) {
29
90
  {#each filters as filter, index}
30
91
  <button
31
92
  class="quick-filters-button"
32
- class:active={filter.active}
93
+ class:active={isActiveFilter({ index })}
33
94
  bind:this={activators[filter.name]}
34
- onclick={() => (openMenus[filter.name] = true)}
95
+ onclick={() => handleClick({ index })}
35
96
  >
36
97
  {#if buttonLabelSnippet}
37
98
  {@render buttonLabelSnippet({ filter })}
@@ -42,7 +103,7 @@ function handleClearFilter(params) {
42
103
  </span>
43
104
  {/if}
44
105
  {filter.label}
45
- {#if filter.active}
106
+ {#if isActiveFilter({ index })}
46
107
  <div
47
108
  class="clear-button"
48
109
  onclick={(e) => {
@@ -154,9 +154,11 @@ function calculateMenuPosition(params) {
154
154
  }
155
155
  }
156
156
  if (flipOnOverflow && !!params.activator) {
157
- let { top: activatorTopDistance } = params.activator.getBoundingClientRect();
158
- if (window.innerHeight < activatorTopDistance + (menuElement?.offsetHeight || 0) + ((menuElement?.offsetHeight || 0) * 0.1)) {
159
- tempTop = getTopDistance(params.activator) - _activatorGap - (menuElement?.offsetHeight || 0);
157
+ if ((anchor == 'bottom' || anchor == 'bottom-center') &&
158
+ tempTop + (menuElement?.offsetHeight || 0) > window.scrollY + window.innerHeight) {
159
+ let { top: activatorTop } = params.activator.getBoundingClientRect();
160
+ let menuHeight = params.menuElement.offsetHeight;
161
+ tempTop = activatorTop + window.scrollY - menuHeight - _activatorGap;
160
162
  }
161
163
  if (anchor == 'right-center' &&
162
164
  window.innerWidth + window.scrollX <
@@ -221,27 +223,6 @@ function calculateMenuPosition(params) {
221
223
  calculatedLeft = tempLeft;
222
224
  }
223
225
  }
224
- function getTopDistance(elem) {
225
- let positionedAncestor = !!menuElement?.parentElement ? getPositionedAncestor(menuElement?.parentElement) : undefined;
226
- if (!!positionedAncestor) {
227
- let top = parseInt(getComputedStyle(positionedAncestor).top);
228
- return (isNaN(top) ? 0 : top) + elem.offsetTop - calcScrollY(elem);
229
- }
230
- else
231
- return window.scrollY + elem.getBoundingClientRect().top;
232
- }
233
- function calcScrollY(elem) {
234
- let parent = elem.parentElement;
235
- let scroll = 0;
236
- while (!!parent) {
237
- scroll += parent.scrollTop;
238
- let parentPosition = getComputedStyle(parent).position;
239
- if (parentPosition === 'absolute' || parentPosition === 'fixed' || parentPosition === 'relative')
240
- break;
241
- parent = parent.parentElement;
242
- }
243
- return scroll;
244
- }
245
226
  let positionedAncestor = undefined;
246
227
  $effect(() => {
247
228
  if (open) {
@@ -11,4 +11,7 @@
11
11
  --autocomplete-default-border-radius: 4px;
12
12
  --autocomplete-default-options-max-width: 100%;
13
13
  --autocomplete-default-input-margin-left: 4px;
14
+ --autocomplete-default-hint-font-size: .75rem;
15
+ --autocomplete-default-hint-color: rgb(var(--global-color-contrast-500), .5);
16
+ --autocomplete-default-hint-margin-left: 2px;
14
17
  }
@@ -4,7 +4,7 @@
4
4
  <script lang="ts" generics="Data">import "../../../css/main.css";
5
5
  import "./Autocomplete.css";
6
6
  import { scrollInMenu } from "../common/scroller";
7
- let { values = $bindable(), items = [], searchFunction = undefined, multiple = false, disabled = false, mandatory = false, placeholder = "", width = "auto", height = "auto", maxWidth = undefined, minWidth = "200px", openingId = $bindable("autocomplete-menu"), searchText = $bindable(), maxVisibleChips = undefined, menuOpened = $bindable(false), closeOnSelect = !multiple, emptySearchTextOnMenuClose = true, menuBoxShadow = "rgb(var(--global-color-background-300), .5) 0px 2px 4px", menuBorderRadius = "5px", mobileDrawer = false, menuWidth = undefined, menuAnchor = 'bottom-center', class: clazz = {}, menuStayInViewport, menuFlipOnOverflow = true, menuMaxHeight = '300px', adaptInputWidth = true, selectionContainerSnippet, selectionSnippet, chipLabelSnippet, exceedCounterSnippet, menuSnippet, itemLabelSnippet, itemSnippet, onchange, onfocus, onblur, onkeydown, onclose, } = $props();
7
+ let { values = $bindable(), items = [], searchFunction = undefined, multiple = false, disabled = false, mandatory = false, placeholder = "", width = "auto", height = "auto", maxWidth = undefined, minWidth = "200px", openingId = $bindable("autocomplete-menu"), searchText = $bindable(), maxVisibleChips = undefined, menuOpened = $bindable(false), closeOnSelect = !multiple, emptySearchTextOnMenuClose = true, menuBoxShadow = "rgb(var(--global-color-background-300), .5) 0px 2px 4px", menuBorderRadius = "5px", mobileDrawer = false, menuWidth = undefined, menuAnchor = 'bottom-center', class: clazz = {}, menuStayInViewport, menuFlipOnOverflow = true, menuMaxHeight = '300px', adaptInputWidth = true, hint, selectionContainerSnippet, selectionSnippet, chipLabelSnippet, exceedCounterSnippet, menuSnippet, itemLabelSnippet, itemSnippet, hintSnippet, onchange, onfocus, onblur, onkeydown, onclose, } = $props();
8
8
  let notVisibleChipNumber = $derived(Math.max(((values || []).length || 0) - (maxVisibleChips || 0), 0));
9
9
  function select(item) {
10
10
  if (disabled)
@@ -188,7 +188,7 @@ $effect(() => {
188
188
  Math.max(searchText?.length || placeholder?.length, 1) + "ch";
189
189
  }
190
190
  else {
191
- input.style.width = "auto";
191
+ input.style.width = "";
192
192
  }
193
193
  }
194
194
  });
@@ -275,6 +275,16 @@ import Icon from "../media/Icon.svelte";
275
275
  bind:this={input}
276
276
  />
277
277
  </div>
278
+
279
+ {#if hintSnippet}
280
+ {@render hintSnippet({ hint })}
281
+ {:else}
282
+ <div class="{clazz?.hint || ''}">
283
+ {#if !!hint}
284
+ <span class="hint">{hint}</span>
285
+ {/if}
286
+ </div>
287
+ {/if}
278
288
  {/if}
279
289
  </div>
280
290
 
@@ -501,4 +511,19 @@ import Icon from "../media/Icon.svelte";
501
511
  border: none;
502
512
  color: inherit;
503
513
  }
514
+
515
+ .hint {
516
+ margin-left: var(
517
+ --autocomplete-hint-margin-left,
518
+ var(--autocomplete-default-hint-margin-left)
519
+ );
520
+ font-size: var(
521
+ --autocomplete-hint-font-size,
522
+ var(--autocomplete-default-hint-font-size)
523
+ );
524
+ color: var(
525
+ --autocomplete-hint-color,
526
+ var(--autocomplete-default-hint-color)
527
+ );
528
+ }
504
529
  </style>
@@ -38,10 +38,12 @@ declare class __sveltets_Render<Data> {
38
38
  menuFlipOnOverflow?: ComponentProps<typeof Menu>["flipOnOverflow"];
39
39
  menuMaxHeight?: string;
40
40
  adaptInputWidth?: boolean;
41
+ hint?: string;
41
42
  class?: {
42
43
  activator?: string;
43
44
  menu?: string;
44
45
  simpleTextfield?: ComponentProps<typeof SimpleTextField>["class"];
46
+ hint?: string;
45
47
  } | undefined;
46
48
  selectionContainerSnippet?: Snippet<[{
47
49
  values: Item<Data>[];
@@ -75,6 +77,9 @@ declare class __sveltets_Render<Data> {
75
77
  index: number;
76
78
  selected: boolean;
77
79
  }]> | undefined;
80
+ hintSnippet?: Snippet<[{
81
+ hint?: string;
82
+ }]> | undefined;
78
83
  onchange?: ((event: {
79
84
  detail: {
80
85
  unselect: Item<Data> | undefined;
@@ -23,7 +23,7 @@ let stickyEnabled = $derived.by(() => {
23
23
  return false;
24
24
  return true;
25
25
  });
26
- const DEFAULT_MIN_WIDTH_PX = 130, DEFAULT_MAX_WIDTH_PX = 400;
26
+ const DEFAULT_MIN_WIDTH_PX = 60, DEFAULT_MAX_WIDTH_PX = 400;
27
27
  onMount(() => {
28
28
  (async () => {
29
29
  await tick();
@@ -38,29 +38,13 @@ onMount(() => {
38
38
  if (tableContainer) {
39
39
  resizeObserver.observe(tableContainer);
40
40
  }
41
- if (appendSnippet && headersHTML['row-append-header']) {
42
- const actionCells = tableContainer?.querySelectorAll('.row-append-cell');
43
- if (actionCells && actionCells.length > 0) {
44
- let maxActionWidth = 0;
45
- for (let i = 0; i < actionCells.length; i++) {
46
- const cellContent = actionCells[i];
47
- const width = cellContent.getBoundingClientRect().width;
48
- if (width > maxActionWidth) {
49
- maxActionWidth = width;
50
- }
51
- }
52
- const finalWidth = Math.max(Math.ceil(maxActionWidth), 40);
53
- headersHTML['row-append-header'].style.width = `${finalWidth}px`;
54
- headersHTML['row-append-header'].style.minWidth = `${finalWidth}px`;
55
- }
56
- }
41
+ resizeRowAppendHeader();
57
42
  for (const head of headers) {
58
43
  let th = headersHTML[head.value];
59
44
  if (!!th) {
60
45
  resizeHeader(th, head);
61
46
  }
62
47
  }
63
- tableHTML?.classList.add('resizable');
64
48
  return () => {
65
49
  resizeObserver?.disconnect();
66
50
  };
@@ -232,7 +216,8 @@ $effect(() => {
232
216
  });
233
217
  async function updateRemainingWidth() {
234
218
  if (tableContainer != null && !!tableContainer && mainHeader) {
235
- const containerWidth = tableContainer?.getBoundingClientRect().width - 10;
219
+ const containerWidth = tableContainer?.getBoundingClientRect().width;
220
+ const scrollbarWidth = tableContainer.offsetWidth - tableContainer.clientWidth;
236
221
  if (containerWidth) {
237
222
  const totalResizableWidth = headers.reduce((sum, head) => {
238
223
  let th = headersHTML[head.value];
@@ -240,17 +225,43 @@ async function updateRemainingWidth() {
240
225
  resizeHeader(th, head);
241
226
  }
242
227
  const width = th?.getBoundingClientRect().width || 0;
243
- return sum + width + 1;
228
+ return sum + width;
244
229
  }, 0);
230
+ resizeRowAppendHeader();
245
231
  const extraStaticWidth = Array.from(mainHeader.querySelectorAll('th.non-resizable, th.row-append-header'))
246
- .reduce((sum, th) => sum + th.getBoundingClientRect().width + 1, 0);
247
- remainingWidth = Math.max(0, containerWidth - totalResizableWidth - extraStaticWidth);
232
+ .reduce((sum, th) => sum + th.getBoundingClientRect().width, 0);
233
+ remainingWidth = Math.max(0, containerWidth - scrollbarWidth - totalResizableWidth - extraStaticWidth);
248
234
  }
249
235
  }
250
236
  }
237
+ function resizeRowAppendHeader() {
238
+ if ((appendSnippet || stickyAppendSnippet) && headersHTML['row-append-header']) {
239
+ if (!!headersHTML['row-append-header'].style.width && headersHTML['row-append-header'].style.width != "0px") {
240
+ return;
241
+ }
242
+ if (tableHTML) {
243
+ tableHTML.style.tableLayout = 'auto';
244
+ }
245
+ let widthWithPadding = headersHTML['row-append-header'].scrollWidth;
246
+ if (tableHTML) {
247
+ tableHTML.style.tableLayout = 'fixed';
248
+ }
249
+ headersHTML['row-append-header'].style.width = `${widthWithPadding}px`;
250
+ headersHTML['row-append-header'].style.minWidth = `${widthWithPadding}px`;
251
+ }
252
+ }
251
253
  function resizeHeader(th, header) {
252
254
  if (!resizedColumnSizeWithPadding[header.value]) {
253
- let widthWithPadding = th.getBoundingClientRect().width;
255
+ if (tableHTML) {
256
+ tableHTML.style.tableLayout = 'auto';
257
+ }
258
+ let widthWithPadding = th.scrollWidth;
259
+ if (widthWithPadding == 0) {
260
+ return;
261
+ }
262
+ if (tableHTML) {
263
+ tableHTML.style.tableLayout = 'fixed';
264
+ }
254
265
  let minWidth = header.minWidth, minWidthPx = DEFAULT_MIN_WIDTH_PX;
255
266
  if (!!minWidth && minWidth.endsWith('px')) {
256
267
  minWidthPx = parseInt(minWidth, 10);
@@ -275,7 +286,7 @@ function resizeHeader(th, header) {
275
286
 
276
287
  {#if !!items && Array.isArray(items)}
277
288
  <div class="simple-table-container {clazz.container || ''}" bind:this={tableContainer}>
278
- <table class="table" bind:this={tableHTML}>
289
+ <table class="table resizable" bind:this={tableHTML}>
279
290
  <thead class="thead {clazz.header || ''}" bind:this={mainHeader}>
280
291
  <tr>
281
292
  {#if prependSnippet}
@@ -365,6 +376,7 @@ function resizeHeader(th, header) {
365
376
  {#if remainingWidth}
366
377
  <th
367
378
  style:width={remainingWidth + 'px'}
379
+ style:padding=0
368
380
  class="filler"
369
381
  aria-hidden="true"
370
382
  ></th>
@@ -460,6 +472,7 @@ function resizeHeader(th, header) {
460
472
  >
461
473
  <div
462
474
  class="row-append-cell"
475
+ style="display: inline-block;"
463
476
  >
464
477
  {@render appendSnippet?.({ index: i, item })}
465
478
  </div>
@@ -656,7 +669,6 @@ function resizeHeader(th, header) {
656
669
  .table.resizable td, th {
657
670
  text-overflow: ellipsis;
658
671
  overflow: hidden;
659
- white-space: nowrap;
660
672
  }
661
673
 
662
674
  th {
@@ -777,7 +789,7 @@ function resizeHeader(th, header) {
777
789
  );
778
790
  }
779
791
  .non-resizable {
780
- padding-left: 0px !important;
792
+ padding: 0px !important;
781
793
  text-align: center;
782
794
  width: var(
783
795
  --simple-table-non-resizable-header-width,
@@ -17,7 +17,7 @@
17
17
  --chip-default-inactive-background-color: rgb(var(--global-color-background-300));
18
18
  --chip-default-inactive-color: rgb(var(--global-color-contrast-900), .8);
19
19
  --chip-default-inactive-hover-background-color: rgb(var(--global-color-primary-500));
20
- --chip-default-inactive-focus-background-color: rgb(var(--global-color-primary-500));
20
+ --chip-default-inactive-focus-background-color: rgb(var(--global-color-background-300));
21
21
  --chip-default-inactive-hover-color: rgb(var(--global-color-contrast-100));
22
22
  --chip-default-inactive-focus-color: rgb(var(--global-color-contrast-100));
23
23
  }
@@ -172,13 +172,6 @@ function handleCloseClick(e) {
172
172
  --chip-inactive-focus-color,
173
173
  var(--chip-default-inactive-focus-color)
174
174
  );
175
- }
176
-
177
- .chip.inactive:focus {
178
- color: var(
179
- --chip-inactive-focus-color,
180
- var(--chip-default-inactive-focus-color)
181
- );
182
175
  border-color: var(
183
176
  --chip-inactive-focus-border-color,
184
177
  var(--chip-default-inactive-border-focus-color)
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.2.3",
4
+ "version": "4.2.5",
5
5
  "scripts": {
6
6
  "host": "vite --host",
7
7
  "dev": "vite dev",