hamzus-ui 0.0.30 → 0.0.32

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,36 +1,130 @@
1
1
  <script>
2
2
  // import
3
- import { tableData } from './table';
3
+ import Checkbox from '../Checkboxes/Checkbox/Checkbox.svelte';
4
+ import { mobileFormat, tableData } from './table';
5
+ import Button from '../Button/Button.svelte';
4
6
  // props
5
7
  export let tableId = null;
6
8
 
7
9
  // local var
8
-
10
+ let extended = {};
11
+ $: numberOfColumn = $tableData[tableId].columns.length ?? 0;
9
12
  // lifeTime component
10
13
 
11
14
  // function
15
+ function handleClick(rowData, avoidClick) {
16
+ if (avoidClick) {
17
+ return;
18
+ }
19
+ // apeller la fonction onclick de la table et fournir la row
20
+ if (
21
+ $tableData[tableId].onClick !== undefined &&
22
+ typeof $tableData[tableId].onClick === 'function'
23
+ ) {
24
+ $tableData[tableId].onClick(rowData);
25
+ }
26
+ }
27
+ function handleSelect(row) {
28
+ // verifier si il est deja present
29
+ let isAlreadySelected = false;
30
+
31
+ for (const pushedRow of $tableData[tableId].selection) {
32
+ if (pushedRow.id === row.id) {
33
+ isAlreadySelected = true;
34
+ break;
35
+ }
36
+ }
37
+
38
+ tableData.update((actualValue) => {
39
+ if (isAlreadySelected) {
40
+ actualValue[tableId].selection = actualValue[tableId].selection.filter(
41
+ (item) => item.id !== row.id
42
+ );
43
+ return actualValue;
44
+ }
45
+ actualValue[tableId].selection.push(row);
46
+ return actualValue;
47
+ });
48
+ }
49
+
50
+ function handleExtend(index) {
51
+ extended[index] = !(extended[index] ?? false);
52
+ }
12
53
  </script>
13
54
 
14
- <div class="content">
15
- {#each $tableData[tableId].rows as row}
16
- <div class="row">
17
- {#each Object.keys(row) as cellKey}
55
+ <div class="content {$mobileFormat ? 'mobile' : ''}">
56
+ {#each $tableData[tableId].rows as row, rowIndex}
57
+ <div class="row {$tableData[tableId].actions.length > 0 ? 'selectable' : ''}">
58
+ {#if $tableData[tableId].actions.length > 0}
59
+ <div
60
+ style="--cell-width:50px"
61
+ class="selector cell {$tableData[tableId].selection.length > 0 ? 'active' : ''}"
62
+ >
63
+ <Checkbox
64
+ size={24}
65
+ checked={$tableData[tableId].selection.filter((item) => item.id === row.id).length > 0}
66
+ onChange={() => {
67
+ handleSelect(row);
68
+ }}
69
+ ></Checkbox>
70
+ </div>
71
+ {/if}
72
+ {#each Object.keys(row) as cellKey, cellIndex}
18
73
  {@const column = $tableData[tableId].columns.filter((obj) => obj.name === cellKey)[0]}
19
74
 
20
75
  {#if !column.hidden}
21
- <div
22
- class="cell"
23
- style="--cell-width:{column.width ?? column.initialWidth}px;{column.skipLine === true
24
- ? 'white-space:nowrap;'
25
- : ''}"
26
- >
27
- <!-- il se peut que ce soit un composant -->
28
- {#if typeof row[cellKey] === 'object'}
29
- <svelte:component this={row[cellKey].component} {...row[cellKey].props}></svelte:component>
30
- {:else}
31
- <h4>{row[cellKey]}</h4>
32
- {/if}
33
- </div>
76
+ {@const cellIsExtended = extended[rowIndex] ?? false}
77
+
78
+ <!-- si le format est mobile que on est a plus de trois cell afficher et que le content est pas extend on affiche pas le reste -->
79
+ {#if !$mobileFormat || numberOfColumn <= 3 || cellIndex < 3 || cellIsExtended}
80
+ <button
81
+ on:click={() => {
82
+ handleClick(row, column.avoidClick ?? false);
83
+ }}
84
+ class="cell"
85
+ style="--cell-width:{column.width ?? column.initialWidth}px;{column.skipLine === true
86
+ ? 'white-space:nowrap;'
87
+ : ''}"
88
+ >
89
+ <!-- il se peut que ce soit un composant -->
90
+ {#if row[cellKey]}
91
+ {#if typeof row[cellKey] === 'object'}
92
+ <svelte:component this={row[cellKey].component} {...row[cellKey].props}
93
+ ></svelte:component>
94
+ {:else}
95
+ <h4>{row[cellKey]}</h4>
96
+ {/if}
97
+ {/if}
98
+ <!-- si c est au format mobile on affiche le nom de la cell -->
99
+ {#if $mobileFormat}
100
+ <h4>{column.label}</h4>
101
+ {/if}
102
+ </button>
103
+ {#if $mobileFormat}
104
+ {#if !cellIsExtended && cellIndex === 2 && numberOfColumn > 3}
105
+ <div class="cell">
106
+ <Button
107
+ variant="outline"
108
+ label="voir plus"
109
+ onClick={() => {
110
+ handleExtend(rowIndex);
111
+ }}
112
+ ></Button>
113
+ </div>
114
+ {/if}
115
+ {#if cellIsExtended && cellIndex === Object.keys(row).length - 1 && numberOfColumn > 3}
116
+ <div class="cell">
117
+ <Button
118
+ variant="outline"
119
+ label="voir moins"
120
+ onClick={() => {
121
+ handleExtend(rowIndex);
122
+ }}
123
+ ></Button>
124
+ </div>
125
+ {/if}
126
+ {/if}
127
+ {/if}
34
128
  {/if}
35
129
  {/each}
36
130
  </div>
@@ -43,17 +137,27 @@
43
137
  flex-direction: column;
44
138
  }
45
139
  .row {
140
+ all: unset;
46
141
  display: flex;
47
142
  border-bottom: 1px solid var(--stroke);
48
- flex-shrink: 0;
49
- min-width: max-content;
50
- width: 100%;
143
+ flex-shrink: 0;
144
+ min-width: max-content;
145
+ width: 100%;
146
+ cursor: pointer;
147
+ position: relative;
51
148
  }
52
149
  .row:hover {
53
- background-color: var(--bg-2);
150
+ background-color: var(--bg-d);
151
+ }
152
+
153
+ .selector {
154
+ display: flex;
155
+ align-items: center;
156
+ justify-content: center;
54
157
  }
55
158
 
56
159
  .cell {
160
+ text-align: left;
57
161
  width: var(--cell-width);
58
162
  padding: var(--pad-m);
59
163
  overflow: hidden;
@@ -61,8 +165,36 @@
61
165
  border-right: 1px solid var(--stroke);
62
166
  flex-shrink: 0;
63
167
  }
168
+ .cell:focus {
169
+ outline: 1px solid var(--accent) !important;
170
+ z-index: 1;
171
+ }
64
172
  .cell > h4 {
65
173
  overflow: hidden;
66
174
  text-overflow: ellipsis;
67
175
  }
176
+
177
+ /* mobile version */
178
+ .mobile{
179
+ width: 100%;
180
+ flex-shrink: 0;
181
+ }
182
+ .mobile .row {
183
+ width: 100%;
184
+ flex-direction: column;
185
+ min-width: unset;
186
+ flex-shrink: 0;
187
+ }
188
+ .mobile .cell {
189
+ width: 100%;
190
+ border-right: 0px;
191
+ display: flex;
192
+ align-items: center;
193
+ column-gap: var(--pad-m);
194
+ justify-content: space-between;
195
+ }
196
+ .mobile .cell:has(:nth-child(2)) > :last-child {
197
+ text-align: right;
198
+ flex-shrink: 0;
199
+ }
68
200
  </style>
@@ -0,0 +1,332 @@
1
+ <script>
2
+ import Button from '../Button/Button.svelte';
3
+ import * as DropdownMenu from '../DropdownMenu';
4
+ import IconButton from '../IconButton/IconButton.svelte';
5
+ import Switch from '../Switch/Switch.svelte';
6
+ import * as Popover from '../Popover';
7
+ import { tableData } from './table';
8
+ import MonthPicker from '../MonthPicker/MonthPicker.svelte';
9
+ import DatePicker from '../DatePicker/DatePicker.svelte';
10
+ import Input from '../Input/Input.svelte';
11
+ import Checkbox from '../Checkboxes/Checkbox/Checkbox.svelte';
12
+
13
+ export let tableId;
14
+
15
+ function handleChangeSkipLine(columnName, newValue) {
16
+ tableData.update((actualValue) => {
17
+ for (let i = 0; i < actualValue[tableId].columns.length; i++) {
18
+ const element = actualValue[tableId].columns[i];
19
+ if (element.name === columnName) {
20
+ actualValue[tableId].columns[i].skipLine = newValue;
21
+ break;
22
+ }
23
+ }
24
+
25
+ return actualValue;
26
+ });
27
+
28
+ document.dispatchEvent(new Event('saveTableData'));
29
+ }
30
+ function handleFilter(columnName, newValue, position) {
31
+ console.log(newValue);
32
+
33
+ let foundedColumn = false;
34
+ tableData.update((actualValue) => {
35
+ for (let i = 0; i < actualValue[tableId].columns.length; i++) {
36
+ const element = actualValue[tableId].columns[i];
37
+ if (element.name === columnName) {
38
+ if (newValue === null || newValue === undefined || newValue === '') {
39
+ actualValue[tableId].columns[i].filter = newValue;
40
+ actualValue[tableId].columns[i].filterAfter = newValue;
41
+ actualValue[tableId].columns[i].filterBefore = newValue;
42
+ delete actualValue[tableId].filters[columnName];
43
+ foundedColumn = true;
44
+ break;
45
+ }
46
+
47
+ switch (position) {
48
+ case 'after':
49
+ // filtre datetime apres le
50
+ actualValue[tableId].columns[i].filterAfter = newValue;
51
+
52
+ break;
53
+ case 'before':
54
+ // filtre datetime apres le
55
+ actualValue[tableId].columns[i].filterBefore = newValue;
56
+ break;
57
+
58
+ default:
59
+ // filtre standart (recherche textuelle)
60
+ actualValue[tableId].columns[i].filter = newValue;
61
+ break;
62
+ }
63
+
64
+ actualValue[tableId].offset = 0;
65
+ actualValue[tableId].filters[columnName] = newValue;
66
+ foundedColumn = true;
67
+ break;
68
+ }
69
+ }
70
+
71
+ return actualValue;
72
+ });
73
+
74
+ if (foundedColumn) {
75
+ // emmetre un event
76
+ document.dispatchEvent(new Event('updateTableResult'));
77
+ }
78
+ }
79
+ function handleSort(columnName) {
80
+ tableData.update((actualValue) => {
81
+ const actualSort = actualValue[tableId].sort;
82
+
83
+ // si la colonne est different alors on set la direction en desc
84
+ let newDirection = '';
85
+ if (actualSort.columnName != columnName) {
86
+ newDirection = 'DESC';
87
+ } else {
88
+ // modifier la direction
89
+ switch (actualSort.direction) {
90
+ case '':
91
+ newDirection = 'DESC';
92
+ break;
93
+ case 'DESC':
94
+ newDirection = 'ASC';
95
+ break;
96
+ default:
97
+ // if "ASC" -> remove filter
98
+ newDirection = '';
99
+ break;
100
+ }
101
+ }
102
+
103
+ actualValue[tableId].sort = {
104
+ columnName: columnName,
105
+ direction: newDirection
106
+ };
107
+
108
+ return actualValue;
109
+ });
110
+ document.dispatchEvent(new Event('updateTableResult'));
111
+ }
112
+ function focusInput(columnName) {
113
+ // selectionner l input si il y en a
114
+
115
+ if (document.querySelector(`.input-column-hamzus-${columnName}`)) {
116
+ document.querySelector(`.input-column-hamzus-${columnName} input`).focus();
117
+ }
118
+ }
119
+ function unfocusInput() {
120
+ // selectionner l input si il y en a
121
+
122
+ if (document.querySelector(`input:focus`)) {
123
+ document.querySelector(`input:focus`).blur();
124
+ }
125
+ }
126
+ function handleSelectAll() {
127
+ tableData.update((actualValue) => {
128
+ if (actualValue[tableId].selection.length !== actualValue[tableId].rows.length) {
129
+ let allRows = actualValue[tableId].rows;
130
+ actualValue[tableId].selection = allRows;
131
+ return actualValue;
132
+ }
133
+
134
+ actualValue[tableId].selection = [];
135
+
136
+ return actualValue;
137
+ });
138
+ }
139
+ </script>
140
+
141
+ {#if $tableData[tableId].actions.length > 0}
142
+ <div
143
+ style="--width:50px"
144
+ class="selector column {$tableData[tableId].selection.length > 0 ? 'active' : ''}"
145
+ >
146
+ <Checkbox
147
+ size={24}
148
+ checked={$tableData[tableId].selection.length === $tableData[tableId].rows.length}
149
+ onChange={handleSelectAll}
150
+ ></Checkbox>
151
+ </div>
152
+ {/if}
153
+ <DropdownMenu.Root>
154
+ <DropdownMenu.Trigger slot="trigger">
155
+ <IconButton variant="outline" style="margin:7px 0px;">
156
+ <svg
157
+ width="24"
158
+ height="24"
159
+ viewBox="0 0 24 24"
160
+ fill="none"
161
+ xmlns="http://www.w3.org/2000/svg"
162
+ >
163
+ <path
164
+ d="M10.9308 22.75C10.4508 22.75 9.97082 22.63 9.53082 22.39C8.64082 21.89 8.11078 21 8.11078 19.99V14.64C8.11078 14.13 7.78076 13.38 7.47076 12.99L3.67078 9.00003C3.04078 8.37003 2.55078 7.27002 2.55078 6.46002V4.14005C2.55078 2.53005 3.7708 1.27002 5.3208 1.27002H18.6608C20.1908 1.27002 21.4308 2.51004 21.4308 4.04004V6.26004C21.4308 7.31004 20.8008 8.52002 20.2008 9.11002C19.9108 9.40002 19.4308 9.40002 19.1408 9.11002C18.8508 8.82002 18.8508 8.34002 19.1408 8.05002C19.5108 7.68002 19.9308 6.85004 19.9308 6.26004V4.04004C19.9308 3.34004 19.3608 2.77002 18.6608 2.77002H5.3208C4.6108 2.77002 4.05078 3.37005 4.05078 4.14005V6.46002C4.05078 6.83002 4.35078 7.56004 4.74078 7.95004L8.59082 12C9.10082 12.63 9.60077 13.69 9.60077 14.64V19.99C9.60077 20.65 10.0508 20.97 10.2508 21.08C10.6808 21.32 11.1908 21.31 11.5908 21.07L12.9908 20.17C13.2808 20 13.5608 19.45 13.5608 19.08C13.5608 18.67 13.9008 18.33 14.3108 18.33C14.7208 18.33 15.0608 18.67 15.0608 19.08C15.0608 19.98 14.5008 21.01 13.7908 21.44L12.4008 22.34C11.9508 22.61 11.4408 22.75 10.9308 22.75Z"
165
+ fill="#292D32"
166
+ />
167
+ <path
168
+ d="M16.0711 17.2701C13.8911 17.2701 12.1211 15.5001 12.1211 13.3201C12.1211 11.1401 13.8911 9.37012 16.0711 9.37012C18.2511 9.37012 20.0211 11.1401 20.0211 13.3201C20.0211 15.5001 18.2511 17.2701 16.0711 17.2701ZM16.0711 10.8701C14.7211 10.8701 13.6211 11.9701 13.6211 13.3201C13.6211 14.6701 14.7211 15.7701 16.0711 15.7701C17.4211 15.7701 18.5211 14.6701 18.5211 13.3201C18.5211 11.9701 17.4211 10.8701 16.0711 10.8701Z"
169
+ fill="#292D32"
170
+ />
171
+ <path
172
+ d="M19.8705 17.8701C19.6805 17.8701 19.4905 17.8001 19.3405 17.6501L18.3405 16.6501C18.0505 16.3601 18.0505 15.8801 18.3405 15.5901C18.6305 15.3001 19.1105 15.3001 19.4005 15.5901L20.4005 16.5901C20.6905 16.8801 20.6905 17.3601 20.4005 17.6501C20.2605 17.7901 20.0605 17.8701 19.8705 17.8701Z"
173
+ fill="#292D32"
174
+ />
175
+ </svg>
176
+ </IconButton>
177
+ </DropdownMenu.Trigger>
178
+ <DropdownMenu.Content slot="content">
179
+ {#each $tableData[tableId].columns as column, index}
180
+ {#if !column.hidden}
181
+ {#if column.isSearchable}
182
+ <DropdownMenu.Root
183
+ triggerFullWidth
184
+ onOpen={() => {
185
+ focusInput(column.name);
186
+ }}
187
+ onClose={unfocusInput}
188
+ >
189
+ <DropdownMenu.Trigger slot="trigger">
190
+ <Button style="width:100%;" avoidRipple={!column.isSearchable} variant="ghost">
191
+ <h4>{column.label}</h4>
192
+ <h4>
193
+ {$tableData[tableId].sort &&
194
+ $tableData[tableId].sort.columnName === column.name &&
195
+ $tableData[tableId].sort.direction !== ''
196
+ ? $tableData[tableId].sort.direction === 'DESC'
197
+ ? '⬇️'
198
+ : '⬆️'
199
+ : ''}
200
+ </h4>
201
+ <h4>
202
+ {$tableData[tableId].filters &&
203
+ ($tableData[tableId].filters[column.name] != '') &
204
+ ($tableData[tableId].filters[column.name] != undefined)
205
+ ? '🔍'
206
+ : ''}
207
+ </h4>
208
+ </Button>
209
+ </DropdownMenu.Trigger>
210
+ <DropdownMenu.Content slot="content">
211
+ <DropdownMenu.Label>{column.label}</DropdownMenu.Label>
212
+ {#if !column.searchChoices}
213
+ {#if column.monthPicker}
214
+ <div class="input-column-hamzus-{column.name}">
215
+ <MonthPicker
216
+ label="mois"
217
+ value={column.filter ?? ''}
218
+ onChange={(newValue) => {
219
+ handleFilter(column.name, newValue);
220
+ }}
221
+ ></MonthPicker>
222
+ </div>
223
+ {:else if column.datePicker}
224
+ <div class="input-column-hamzus-{column.name}">
225
+ <DatePicker
226
+ label="à partir du"
227
+ value={column.filterAfter ?? ''}
228
+ onChange={(newValue) => {
229
+ handleFilter(column.name, newValue, 'after');
230
+ }}
231
+ ></DatePicker>
232
+ <DatePicker
233
+ label="jusqu'au"
234
+ value={column.filterBefore ?? ''}
235
+ onChange={(newValue) => {
236
+ handleFilter(column.name, newValue, 'before');
237
+ }}
238
+ ></DatePicker>
239
+ </div>
240
+ {:else}
241
+ <div class="input-column-hamzus-{column.name}">
242
+ <Input
243
+ placeholder="filtre"
244
+ value={column.filter ?? ''}
245
+ onChange={(newValue) => {
246
+ handleFilter(column.name, newValue);
247
+ }}
248
+ ></Input>
249
+ </div>
250
+ {/if}
251
+ {:else}
252
+ <Popover.Root direction="right" triggerFullWidth>
253
+ <Popover.Trigger slot="trigger">
254
+ <Button style="width:100%;justify-content:space-between;" variant="ghost">
255
+ <h4>filtre</h4>
256
+ <svg
257
+ width="24"
258
+ height="24"
259
+ viewBox="0 0 24 24"
260
+ fill="none"
261
+ xmlns="http://www.w3.org/2000/svg"
262
+ >
263
+ <path
264
+ d="M14.4291 18.8201C14.2391 18.8201 14.0491 18.7501 13.8991 18.6001C13.6091 18.3101 13.6091 17.8301 13.8991 17.5401L19.4391 12.0001L13.8991 6.46012C13.6091 6.17012 13.6091 5.69012 13.8991 5.40012C14.1891 5.11012 14.6691 5.11012 14.9591 5.40012L21.0291 11.4701C21.3191 11.7601 21.3191 12.2401 21.0291 12.5301L14.9591 18.6001C14.8091 18.7501 14.6191 18.8201 14.4291 18.8201Z"
265
+ fill="#292D32"
266
+ />
267
+ <path
268
+ d="M20.33 12.75H3.5C3.09 12.75 2.75 12.41 2.75 12C2.75 11.59 3.09 11.25 3.5 11.25H20.33C20.74 11.25 21.08 11.59 21.08 12C21.08 12.41 20.74 12.75 20.33 12.75Z"
269
+ fill="#292D32"
270
+ />
271
+ </svg>
272
+ </Button>
273
+ </Popover.Trigger>
274
+ <Popover.Content slot="content">
275
+ <Popover.Button
276
+ label="aucun"
277
+ onClick={() => {
278
+ handleFilter(column.name, null);
279
+ }}
280
+ ></Popover.Button>
281
+ {#each column.searchChoices as choice}
282
+ <Popover.Button
283
+ label={choice.label}
284
+ onClick={() => {
285
+ handleFilter(column.name, choice.value);
286
+ }}
287
+ ></Popover.Button>
288
+ {/each}
289
+ </Popover.Content>
290
+ </Popover.Root>
291
+ {/if}
292
+ <DropdownMenu.Separator></DropdownMenu.Separator>
293
+ <DropdownMenu.Button variant="ghost" style="padding:0;">
294
+ <label
295
+ style="display:flex;align-items:center;justify-content:space-between;width:100%;padding:3px 7px;"
296
+ >
297
+ <h4>ne pas passer a la ligne</h4>
298
+ <Switch
299
+ checked={column.skipLine}
300
+ onChange={(newValue) => {
301
+ handleChangeSkipLine(column.name, newValue);
302
+ }}
303
+ size={16}
304
+ ></Switch>
305
+ </label>
306
+ </DropdownMenu.Button>
307
+ {#if column.isSortable !== false}
308
+ <DropdownMenu.Separator></DropdownMenu.Separator>
309
+ <DropdownMenu.Button
310
+ onClick={() => {
311
+ handleSort(column.name);
312
+ }}
313
+ variant="ghost"
314
+ label="trier"
315
+ ></DropdownMenu.Button>
316
+ {/if}
317
+ </DropdownMenu.Content>
318
+ </DropdownMenu.Root>
319
+ {:else}
320
+ <div style="width:100%;">
321
+ <Button
322
+ style="width:100%;"
323
+ avoidRipple={!column.isSearchable}
324
+ variant="ghost"
325
+ label={column.label}
326
+ ></Button>
327
+ </div>
328
+ {/if}
329
+ {/if}
330
+ {/each}
331
+ </DropdownMenu.Content>
332
+ </DropdownMenu.Root>