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.
@@ -7,10 +7,14 @@
7
7
  import Input from '../Input/Input.svelte';
8
8
  import RadioGroup from '../Radios/RadioGroup/RadioGroup.svelte';
9
9
  import Switch from '../Switch/Switch.svelte';
10
+ import { onDestroy } from 'svelte';
11
+ import DatePicker from '../DatePicker/DatePicker.svelte';
12
+ import MonthPicker from '../MonthPicker/MonthPicker.svelte';
13
+ import Checkbox from '../Checkboxes/Checkbox/Checkbox.svelte';
10
14
  // props
11
15
  export let tableId = null;
12
16
  // local var
13
- const minimalColumnWidth = 50
17
+ const minimalColumnWidth = 50;
14
18
  let start = null;
15
19
  let trackedColumnName = null;
16
20
  let trackedColumnWidth = null;
@@ -21,8 +25,8 @@
21
25
  for (let i = 0; i < $tableData[tableId].columns.length; i++) {
22
26
  const element = $tableData[tableId].columns[i];
23
27
  if (element.name === trackedColumnName) {
24
- trackedColumnWidth = element.width ?? element.initialWidth ?? 0
25
- break
28
+ trackedColumnWidth = element.width ?? element.initialWidth ?? 0;
29
+ break;
26
30
  }
27
31
  }
28
32
  document.addEventListener('mousemove', handleMouseMove);
@@ -36,9 +40,10 @@
36
40
  for (let i = 0; i < actualValue[tableId].columns.length; i++) {
37
41
  const element = actualValue[tableId].columns[i];
38
42
  if (element.name === trackedColumnName) {
39
- const newWidth = trackedColumnWidth - deltaX
40
- actualValue[tableId].columns[i].width = newWidth > minimalColumnWidth ? newWidth : minimalColumnWidth;
41
- break;
43
+ const newWidth = trackedColumnWidth - deltaX;
44
+ actualValue[tableId].columns[i].width =
45
+ newWidth > minimalColumnWidth ? newWidth : minimalColumnWidth;
46
+ break;
42
47
  }
43
48
  }
44
49
 
@@ -48,114 +53,218 @@
48
53
  function removeListener() {
49
54
  document.removeEventListener('mousemove', handleMouseMove);
50
55
  document.removeEventListener('mouseup', removeListener);
56
+
57
+ document.dispatchEvent(new Event('saveTableData'));
51
58
  }
52
- function handleChangeSkipLine(columnName, newValue) {
53
- tableData.update((actualValue) => {
59
+ function handleChangeSkipLine(columnName, newValue) {
60
+ tableData.update((actualValue) => {
54
61
  for (let i = 0; i < actualValue[tableId].columns.length; i++) {
55
62
  const element = actualValue[tableId].columns[i];
56
63
  if (element.name === columnName) {
57
64
  actualValue[tableId].columns[i].skipLine = newValue;
58
- break;
65
+ break;
59
66
  }
60
67
  }
61
68
 
62
69
  return actualValue;
63
70
  });
64
- }
65
- function handleFilter(columnName, newValue) {
66
-
67
- let foundedColumn = false
68
- tableData.update((actualValue) => {
71
+
72
+ document.dispatchEvent(new Event('saveTableData'));
73
+ }
74
+ function handleFilter(columnName, newValue, position) {
75
+ console.log(newValue);
76
+
77
+ let foundedColumn = false;
78
+ tableData.update((actualValue) => {
69
79
  for (let i = 0; i < actualValue[tableId].columns.length; i++) {
70
80
  const element = actualValue[tableId].columns[i];
71
81
  if (element.name === columnName) {
72
- if (newValue === null || newValue === undefined || newValue === "") {
73
- actualValue[tableId].columns[i].filter = newValue
74
- delete actualValue[tableId].filters[columnName]
75
- foundedColumn = true
76
- break
77
- }
78
- actualValue[tableId].columns[i].filter = newValue
79
- actualValue[tableId].filters[columnName] = newValue
80
- foundedColumn = true
81
- break;
82
+ if (newValue === null || newValue === undefined || newValue === '') {
83
+ actualValue[tableId].columns[i].filter = newValue;
84
+ actualValue[tableId].columns[i].filterAfter = newValue;
85
+ actualValue[tableId].columns[i].filterBefore = newValue;
86
+ delete actualValue[tableId].filters[columnName];
87
+ foundedColumn = true;
88
+ break;
89
+ }
90
+
91
+ switch (position) {
92
+ case 'after':
93
+ // filtre datetime apres le
94
+ actualValue[tableId].columns[i].filterAfter = newValue;
95
+
96
+ break;
97
+ case 'before':
98
+ // filtre datetime apres le
99
+ actualValue[tableId].columns[i].filterBefore = newValue;
100
+ break;
101
+
102
+ default:
103
+ // filtre standart (recherche textuelle)
104
+ actualValue[tableId].columns[i].filter = newValue;
105
+ break;
106
+ }
107
+
108
+ actualValue[tableId].offset = 0;
109
+ actualValue[tableId].filters[columnName] = newValue;
110
+ foundedColumn = true;
111
+ break;
82
112
  }
83
113
  }
84
114
 
85
115
  return actualValue;
86
116
  });
87
-
88
- if (foundedColumn) {
89
- // emmetre un event
90
- document.dispatchEvent(new Event("updateTableResult"))
91
- }
92
- }
93
- function handleSort(columnName){
94
- tableData.update((actualValue) => {
95
- const actualSort = actualValue[tableId].sort
96
117
 
97
- // si la colonne est different alors on set la direction en desc
98
- let newDirection = ""
99
- if (actualSort.columnName != columnName) {
100
- newDirection = "DESC"
101
- }else{
102
- // modifier la direction
103
- switch (actualSort.direction) {
104
- case "":
105
- newDirection = "DESC"
106
- break;
107
- case "DESC":
108
- newDirection = "ASC"
109
- break;
110
- default:
111
- // if "ASC" -> remove filter
112
- newDirection = ""
113
- break;
114
- }
115
- }
118
+ if (foundedColumn) {
119
+ // emmetre un event
120
+ document.dispatchEvent(new Event('updateTableResult'));
121
+ }
122
+ }
123
+ function handleSort(columnName) {
124
+ tableData.update((actualValue) => {
125
+ const actualSort = actualValue[tableId].sort;
126
+
127
+ // si la colonne est different alors on set la direction en desc
128
+ let newDirection = '';
129
+ if (actualSort.columnName != columnName) {
130
+ newDirection = 'DESC';
131
+ } else {
132
+ // modifier la direction
133
+ switch (actualSort.direction) {
134
+ case '':
135
+ newDirection = 'DESC';
136
+ break;
137
+ case 'DESC':
138
+ newDirection = 'ASC';
139
+ break;
140
+ default:
141
+ // if "ASC" -> remove filter
142
+ newDirection = '';
143
+ break;
144
+ }
145
+ }
116
146
 
117
147
  actualValue[tableId].sort = {
118
- "columnName":columnName,
119
- "direction":newDirection
120
- }
148
+ columnName: columnName,
149
+ direction: newDirection
150
+ };
121
151
 
122
152
  return actualValue;
123
153
  });
124
- document.dispatchEvent(new Event("updateTableResult"))
125
- }
126
- function focusInput(columnName) {
127
- // selectionner l input si il y en a
128
-
129
- if (document.querySelector(`.input-column-hamzus-${columnName}`)) {
130
- document.querySelector(`.input-column-hamzus-${columnName} input`).focus()
131
- }
154
+ document.dispatchEvent(new Event('updateTableResult'));
155
+ }
156
+ function focusInput(columnName) {
157
+ // selectionner l input si il y en a
158
+
159
+ if (document.querySelector(`.input-column-hamzus-${columnName}`)) {
160
+ document.querySelector(`.input-column-hamzus-${columnName} input`).focus();
161
+ }
162
+ }
163
+ function unfocusInput() {
164
+ // selectionner l input si il y en a
165
+
166
+ if (document.querySelector(`input:focus`)) {
167
+ document.querySelector(`input:focus`).blur();
168
+ }
169
+ }
170
+ function handleSelectAll() {
171
+ tableData.update((actualValue)=>{
172
+ if (actualValue[tableId].selection.length !== actualValue[tableId].rows.length) {
173
+ let allRows = actualValue[tableId].rows
174
+ actualValue[tableId].selection = allRows
175
+ return actualValue
176
+ }
177
+
178
+ actualValue[tableId].selection = []
179
+
180
+ return actualValue
181
+ })
132
182
  }
133
183
  </script>
134
184
 
135
- <div class="header">
185
+ <div class="header {$tableData[tableId].actions.length > 0 ? 'selectable' : ''}">
186
+ {#if $tableData[tableId].actions.length > 0}
187
+ <div style="--width:50px" class="selector column {$tableData[tableId].selection.length > 0 ? 'active' : ''}">
188
+ <Checkbox
189
+ size={24}
190
+ checked={$tableData[tableId].selection.length === $tableData[tableId].rows.length}
191
+ onChange={handleSelectAll}
192
+ ></Checkbox>
193
+ </div>
194
+ {/if}
136
195
  {#each $tableData[tableId].columns as column, index}
137
196
  {#if !column.hidden}
138
197
  <div class="column" style="--width:{column.width ?? column.initialWidth}px;">
139
198
  {#if column.isSearchable}
140
- <DropdownMenu.Root triggerFullWidth onOpen={()=>{focusInput(column.name)}}>
199
+ <DropdownMenu.Root
200
+ triggerFullWidth
201
+ onOpen={() => {
202
+ focusInput(column.name);
203
+ }}
204
+ onClose={unfocusInput}
205
+ >
141
206
  <DropdownMenu.Trigger slot="trigger">
142
- <Button
143
- style="width:100%;"
144
- avoidRipple={!column.isSearchable}
145
- variant="ghost"
146
- >
147
- <h4>{column.label}</h4>
148
- <h4>{($tableData[tableId].sort && $tableData[tableId].sort.columnName === column.name && $tableData[tableId].sort.direction !== "" ? ($tableData[tableId].sort.direction === "DESC" ? "⬇️" : "⬆️") : "")}</h4>
149
- <h4>{$tableData[tableId].filters && $tableData[tableId].filters[column.name] != "" & $tableData[tableId].filters[column.name] != undefined ? "🔍" : ""}</h4>
150
-
151
- </Button>
207
+ <Button style="width:100%;" avoidRipple={!column.isSearchable} variant="ghost">
208
+ <h4>{column.label}</h4>
209
+ <h4>
210
+ {$tableData[tableId].sort &&
211
+ $tableData[tableId].sort.columnName === column.name &&
212
+ $tableData[tableId].sort.direction !== ''
213
+ ? $tableData[tableId].sort.direction === 'DESC'
214
+ ? '⬇️'
215
+ : '⬆️'
216
+ : ''}
217
+ </h4>
218
+ <h4>
219
+ {$tableData[tableId].filters &&
220
+ ($tableData[tableId].filters[column.name] != '') &
221
+ ($tableData[tableId].filters[column.name] != undefined)
222
+ ? '🔍'
223
+ : ''}
224
+ </h4>
225
+ </Button>
152
226
  </DropdownMenu.Trigger>
153
227
  <DropdownMenu.Content slot="content">
154
228
  <DropdownMenu.Label>{column.label}</DropdownMenu.Label>
155
229
  {#if !column.searchChoices}
156
- <div class="input-column-hamzus-{column.name}">
157
- <Input placeholder="filtre" value={column.filter} onChange={(newValue)=>{handleFilter(column.name, newValue)}}></Input>
158
- </div>
230
+ {#if column.monthPicker}
231
+ <div class="input-column-hamzus-{column.name}">
232
+ <MonthPicker
233
+ label="mois"
234
+ value={column.filter ?? ''}
235
+ onChange={(newValue) => {
236
+ handleFilter(column.name, newValue);
237
+ }}
238
+ ></MonthPicker>
239
+ </div>
240
+ {:else if column.datePicker}
241
+ <div class="input-column-hamzus-{column.name}">
242
+ <DatePicker
243
+ label="à partir du"
244
+ value={column.filterAfter ?? ''}
245
+ onChange={(newValue) => {
246
+ handleFilter(column.name, newValue, 'after');
247
+ }}
248
+ ></DatePicker>
249
+ <DatePicker
250
+ label="jusqu'au"
251
+ value={column.filterBefore ?? ''}
252
+ onChange={(newValue) => {
253
+ handleFilter(column.name, newValue, 'before');
254
+ }}
255
+ ></DatePicker>
256
+ </div>
257
+ {:else}
258
+ <div class="input-column-hamzus-{column.name}">
259
+ <Input
260
+ placeholder="filtre"
261
+ value={column.filter ?? ''}
262
+ onChange={(newValue) => {
263
+ handleFilter(column.name, newValue);
264
+ }}
265
+ ></Input>
266
+ </div>
267
+ {/if}
159
268
  {:else}
160
269
  <Popover.Root direction="right" triggerFullWidth>
161
270
  <Popover.Trigger slot="trigger">
@@ -180,39 +289,63 @@
180
289
  </Button>
181
290
  </Popover.Trigger>
182
291
  <Popover.Content slot="content">
183
- <Popover.Button label="aucun" onClick={()=>{handleFilter(column.name, null)}}></Popover.Button>
292
+ <Popover.Button
293
+ label="aucun"
294
+ onClick={() => {
295
+ handleFilter(column.name, null);
296
+ }}
297
+ ></Popover.Button>
184
298
  {#each column.searchChoices as choice}
185
- <Popover.Button label={choice.label} onClick={()=>{handleFilter(column.name, choice.value)}}></Popover.Button>
299
+ <Popover.Button
300
+ label={choice.label}
301
+ onClick={() => {
302
+ handleFilter(column.name, choice.value);
303
+ }}
304
+ ></Popover.Button>
186
305
  {/each}
187
306
  </Popover.Content>
188
307
  </Popover.Root>
189
308
  {/if}
190
309
  <DropdownMenu.Separator></DropdownMenu.Separator>
191
- <DropdownMenu.Button variant="ghost">
310
+ <DropdownMenu.Button variant="ghost" style="padding:0;">
192
311
  <label
193
- style="display:flex;align-items:center;justify-content:space-between;width:100%;"
312
+ style="display:flex;align-items:center;justify-content:space-between;width:100%;padding:3px 7px;"
194
313
  >
195
- <h4>passer a la ligne</h4>
196
- <Switch checked={column.skipLine} onChange={(newValue)=>{handleChangeSkipLine(column.name, newValue)}} size={16}></Switch>
314
+ <h4>ne pas passer a la ligne</h4>
315
+ <Switch
316
+ checked={column.skipLine}
317
+ onChange={(newValue) => {
318
+ handleChangeSkipLine(column.name, newValue);
319
+ }}
320
+ size={16}
321
+ ></Switch>
197
322
  </label>
198
323
  </DropdownMenu.Button>
199
324
  {#if column.isSortable !== false}
200
325
  <DropdownMenu.Separator></DropdownMenu.Separator>
201
- <DropdownMenu.Button onClick={()=>{handleSort(column.name)}} variant="ghost" label="trier"></DropdownMenu.Button>
326
+ <DropdownMenu.Button
327
+ onClick={() => {
328
+ handleSort(column.name);
329
+ }}
330
+ variant="ghost"
331
+ label="trier"
332
+ ></DropdownMenu.Button>
202
333
  {/if}
203
334
  </DropdownMenu.Content>
204
335
  </DropdownMenu.Root>
205
336
  {:else}
206
- <Button
207
- style="width:100%;"
208
- avoidRipple={!column.isSearchable}
209
- variant="ghost"
210
- label={column.label}
211
- ></Button>
337
+ <div style="width:100%;">
338
+ <Button
339
+ style="width:100%;"
340
+ avoidRipple={!column.isSearchable}
341
+ variant="ghost"
342
+ label={column.label}
343
+ ></Button>
344
+ </div>
212
345
  {/if}
213
346
  <span class="resizer"
214
347
  ><button
215
- onmousedown={(event) => {
348
+ on:mousedown={(event) => {
216
349
  handleMouseDown(event, column.name);
217
350
  }}
218
351
  class="resizer-pad"
@@ -230,7 +363,7 @@
230
363
  display: flex;
231
364
  border-bottom: 1px solid var(--stroke);
232
365
  user-select: none;
233
- background-color: var(--bg-2);
366
+ background-color: var(--bg-d);
234
367
  position: sticky;
235
368
  top: 0;
236
369
  z-index: 1;
@@ -240,6 +373,12 @@
240
373
  display: flex;
241
374
  width: var(--width);
242
375
  }
376
+ .selector{
377
+ display: flex;
378
+ align-items: center;
379
+ justify-content: center;
380
+ }
381
+
243
382
 
244
383
  /* track */
245
384
  .resizer {
@@ -1,7 +1,9 @@
1
1
  <script>
2
2
  // import
3
- import { onMount } from 'svelte';
4
- import { tableData } from './table';
3
+ import { onDestroy, onMount } from 'svelte';
4
+ import { mobileFormat, tableData } from './table';
5
+ import { writable } from 'svelte/store';
6
+ import TableLoader from './TableLoader.svelte';
5
7
 
6
8
  // props
7
9
  export let tableId = 0;
@@ -14,20 +16,48 @@
14
16
  export let limit = 20;
15
17
  export let offset = 0;
16
18
  export let getRows = (globalSearch, filters) => {};
19
+ export let onClick = undefined;
20
+ export let version = 1;
17
21
 
18
22
  // locale var
19
- let monted = false;
23
+ let mounted = false;
24
+ let loading = false;
20
25
  let rows = [];
21
26
  let defaultColumns = [...columns];
27
+ let selection = [];
22
28
 
23
29
  // life component
24
30
  onMount(async () => {
31
+ // verifier le type d agencement
32
+ const savedFormat = localStorage.getItem(`table_format`);
33
+
34
+ if (!savedFormat) {
35
+ // verifier la taille de l ecran pour savoir si on est sur telephgone 900px
36
+ if (window.innerWidth < 900) {
37
+ mobileFormat.set(true)
38
+ }
39
+
40
+ localStorage.setItem(`table_format`, $mobileFormat ? "1" : "0");
41
+ }else {
42
+ $mobileFormat = savedFormat === "1"
43
+ }
44
+
45
+
25
46
  // verifier si l utilisateur possede une ocnfigursation enregistre dans le localsotrage
26
47
  const savedConfiguration = localStorage.getItem(`table_${tableId}`);
48
+
27
49
  if (savedConfiguration) {
28
50
  try {
29
51
  const overrideConfig = JSON.parse(savedConfiguration);
30
- columns = overrideConfig.columns;
52
+
53
+ const upToDate = version === (overrideConfig.version ?? null)
54
+
55
+ // si il n'est pas a jour alors il doit étre surpimmer le localstorage et le mettre a jour
56
+ if (!upToDate) {
57
+ localStorage.removeItem(`table_${tableId}`)
58
+ }else{
59
+ columns = overrideConfig.columns;
60
+ }
31
61
  filters = overrideConfig.filters;
32
62
  globalSearch = overrideConfig.globalSearch;
33
63
  } catch (error) {
@@ -53,7 +83,10 @@
53
83
  totalOfRows: totalOfRows,
54
84
  limit: limit,
55
85
  offset: offset,
56
- getRows: getRows
86
+ getRows: getRows,
87
+ onClick:onClick,
88
+ version:version,
89
+ selection:selection
57
90
  };
58
91
 
59
92
  tableData.update((actualValue) => {
@@ -68,33 +101,81 @@
68
101
 
69
102
  // add les event
70
103
  document.addEventListener('updateTableResult', handleUpdateResult);
104
+ document.addEventListener('saveTableData', saveDataActualData);
71
105
  document.addEventListener('resetTableColumnsSettings', handleResetSettings);
72
106
 
73
- monted = true;
107
+ mounted = true;
74
108
 
75
109
  return () => {
110
+ mounted = false;
76
111
  document.removeEventListener('updateTableResult', handleUpdateResult);
112
+ document.removeEventListener('saveTableData', saveDataActualData);
77
113
  document.removeEventListener('resetTableColumnsSettings', handleResetSettings);
78
114
  };
79
115
  });
116
+ onDestroy(()=>{
117
+ mounted = false;
118
+ })
80
119
 
81
120
  async function handleUpdateResult() {
121
+ if (!mounted || loading) {
122
+ return
123
+ }
124
+ loading = true
125
+
82
126
  const actualTableData = $tableData[tableId];
83
127
 
128
+ let filters = {
129
+ ...actualTableData.filters
130
+ }
131
+
132
+ // vertifier si il n y a pas de filtre special style datepicker pour interval de temsp
133
+ for (const column of actualTableData.columns) {
134
+ if (!column.datePicker ) {
135
+ continue;
136
+ }
137
+ if (filters[column.name]) {
138
+ delete filters[column.name]
139
+ }
140
+ if (column.filterAfter) {
141
+ filters = {
142
+ ...filters,
143
+ [column.name + "_after"]:column.filterAfter
144
+ }
145
+ }
146
+ if (column.filterBefore) {
147
+ filters = {
148
+ ...filters,
149
+ [column.name + "_before"]:column.filterBefore
150
+ }
151
+ }
152
+ }
153
+
84
154
  const request = await getRows(
85
155
  actualTableData.globalSearch,
86
- actualTableData.filters,
156
+ filters,
87
157
  actualTableData.sort,
88
158
  actualTableData.limit,
89
159
  actualTableData.offset
90
160
  );
91
161
  totalOfRows = request.totalOfRows
92
- $tableData[tableId].rows = request.rows;
162
+ tableData.update((actualTable)=>{
163
+ if (!actualTable[tableId]) {
164
+ return actualTable
165
+ }
166
+ actualTable[tableId].rows = request.rows
167
+ actualTable[tableId].totalOfRows = request.totalOfRows
168
+ return actualTable
169
+ })
93
170
 
94
171
  saveDataActualData();
172
+
173
+ loading = false
174
+ // setTimeout(()=>{
175
+ // },1000)
95
176
  }
96
177
  async function saveDataActualData() {
97
- if (!monted) {
178
+ if (!mounted) {
98
179
  return;
99
180
  }
100
181
  localStorage.setItem(`table_${tableId}`, JSON.stringify($tableData[tableId]));
@@ -106,14 +187,23 @@
106
187
  ...actualValue
107
188
  };
108
189
  });
190
+ saveDataActualData()
109
191
  }
110
192
  </script>
111
193
 
112
- {#if monted}
194
+ {#if mounted}
113
195
  <div class="table">
114
- <slot name="actions-bar" {tableId}></slot>
115
- <slot name="header" {tableId}></slot>
116
- <slot name="content" {tableId}></slot>
196
+ <div class="top-bar">
197
+ <slot name="actions-bar" {tableId}></slot>
198
+ {#if !$mobileFormat}
199
+ <slot name="header" {tableId}></slot>
200
+ {/if}
201
+ </div>
202
+ {#if loading}
203
+ <TableLoader></TableLoader>
204
+ {:else}
205
+ <slot name="content" {tableId}></slot>
206
+ {/if}
117
207
  <slot name="navigation-bar" {tableId}></slot>
118
208
  </div>
119
209
  {/if}
@@ -123,6 +213,11 @@
123
213
  display: flex;
124
214
  flex-direction: column;
125
215
  width: 100%;
126
- overflow-x: scroll;
127
216
  }
217
+ .top-bar{
218
+ position: sticky;
219
+ top: 0;
220
+ background-color: var(--bg-u);
221
+ z-index: 1;
222
+ }
128
223
  </style>