@ouestfrance/sipa-bms-ui 8.48.2 → 8.49.0
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.
- package/dist/components/table/BmsTable.vue.d.ts +3 -0
- package/dist/components/table/UiBmsTable.vue.d.ts +15 -10
- package/dist/components/table/UiBmsTableRow.vue.d.ts +18 -8
- package/dist/sipa-bms-ui.css +71 -50
- package/dist/sipa-bms-ui.es.js +236 -138
- package/dist/sipa-bms-ui.es.js.map +1 -1
- package/dist/sipa-bms-ui.umd.js +236 -138
- package/dist/sipa-bms-ui.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/components/table/BmsTable.stories.js +70 -0
- package/src/components/table/BmsTable.vue +24 -3
- package/src/components/table/UiBmsTable.stories.js +40 -0
- package/src/components/table/UiBmsTable.vue +74 -7
- package/src/components/table/UiBmsTableRow.vue +41 -3
package/package.json
CHANGED
|
@@ -3,6 +3,7 @@ import BmsButton from '@/components/button/BmsButton.vue';
|
|
|
3
3
|
import BmsIconButton from '@/components/button/BmsIconButton.vue';
|
|
4
4
|
import { BmsTag } from '@/index';
|
|
5
5
|
import { Save, Trash, Pencil } from 'lucide-vue-next';
|
|
6
|
+
import { ref } from 'vue';
|
|
6
7
|
|
|
7
8
|
export default {
|
|
8
9
|
title: 'Composants/table/Table',
|
|
@@ -1398,3 +1399,72 @@ DoWithRowSelection.args = {
|
|
|
1398
1399
|
selectable: true,
|
|
1399
1400
|
selectedItems: [],
|
|
1400
1401
|
};
|
|
1402
|
+
|
|
1403
|
+
const players = [
|
|
1404
|
+
{ name: 'Maignan', position: 'Gardien' },
|
|
1405
|
+
{ name: 'Pavard', position: 'Défenseur' },
|
|
1406
|
+
{ name: 'Upamecano', position: 'Défenseur' },
|
|
1407
|
+
{ name: 'Hernandez', position: 'Défenseur' },
|
|
1408
|
+
{ name: 'Camavinga', position: 'Milieu' },
|
|
1409
|
+
{ name: 'Tchouameni', position: 'Milieu' },
|
|
1410
|
+
{ name: 'Griezmann', position: 'Milieu' },
|
|
1411
|
+
{ name: 'Mbappé', position: 'Attaquant' },
|
|
1412
|
+
];
|
|
1413
|
+
|
|
1414
|
+
const draggableHeaders = [
|
|
1415
|
+
{ key: 'name', label: 'Joueur' },
|
|
1416
|
+
{ key: 'position', label: 'Poste' },
|
|
1417
|
+
];
|
|
1418
|
+
|
|
1419
|
+
const DraggableTemplate = () => ({
|
|
1420
|
+
components: { BmsTable },
|
|
1421
|
+
setup() {
|
|
1422
|
+
const items = ref([...players]);
|
|
1423
|
+
const onReorder = (reordered) => { items.value = reordered; };
|
|
1424
|
+
return { items, onReorder, draggableHeaders };
|
|
1425
|
+
},
|
|
1426
|
+
template: `
|
|
1427
|
+
<div>
|
|
1428
|
+
<BmsTable
|
|
1429
|
+
:items="items"
|
|
1430
|
+
:headers="draggableHeaders"
|
|
1431
|
+
:disable-search="true"
|
|
1432
|
+
draggable
|
|
1433
|
+
@reorder="onReorder"
|
|
1434
|
+
/>
|
|
1435
|
+
<pre style="margin-top: 1rem; font-size: 0.75rem; color: #666;">Ordre actuel : {{ items.map(i => i.name).join(' → ') }}</pre>
|
|
1436
|
+
</div>
|
|
1437
|
+
`,
|
|
1438
|
+
});
|
|
1439
|
+
|
|
1440
|
+
export const Draggable = DraggableTemplate.bind({});
|
|
1441
|
+
Draggable.storyName = 'Draggable (réordonnancement)';
|
|
1442
|
+
|
|
1443
|
+
const DraggableSelectableTemplate = () => ({
|
|
1444
|
+
components: { BmsTable },
|
|
1445
|
+
setup() {
|
|
1446
|
+
const items = ref([...players]);
|
|
1447
|
+
const selectedItems = ref([]);
|
|
1448
|
+
const onReorder = (reordered) => { items.value = reordered; };
|
|
1449
|
+
return { items, selectedItems, onReorder, draggableHeaders };
|
|
1450
|
+
},
|
|
1451
|
+
template: `
|
|
1452
|
+
<div>
|
|
1453
|
+
<BmsTable
|
|
1454
|
+
v-model:selectedItems="selectedItems"
|
|
1455
|
+
:items="items"
|
|
1456
|
+
:headers="draggableHeaders"
|
|
1457
|
+
:disable-search="true"
|
|
1458
|
+
selectable
|
|
1459
|
+
draggable
|
|
1460
|
+
@reorder="onReorder"
|
|
1461
|
+
/>
|
|
1462
|
+
<pre style="margin-top: 1rem; font-size: 0.75rem; color: #666;">Ordre : {{ items.map(i => i.name).join(' → ') }}
|
|
1463
|
+
Sélection : {{ selectedItems.map(i => i.name).join(', ') || '—' }}</pre>
|
|
1464
|
+
</div>
|
|
1465
|
+
`,
|
|
1466
|
+
});
|
|
1467
|
+
|
|
1468
|
+
export const DraggableSelectable = DraggableSelectableTemplate.bind({});
|
|
1469
|
+
DraggableSelectable.storyName = 'Draggable + Selectable';
|
|
1470
|
+
DraggableSelectable.parameters = { chromatic: { disable: true } };
|
|
@@ -45,6 +45,7 @@ interface UiTableProps {
|
|
|
45
45
|
selectableDisabled?: boolean;
|
|
46
46
|
selectMode?: SelectMode.DEFAULT | SelectMode.SINGLE;
|
|
47
47
|
customSearch?: (item: unknown, searchValue: string) => boolean;
|
|
48
|
+
draggable?: boolean;
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
const props = withDefaults(defineProps<UiTableProps>(), {
|
|
@@ -105,6 +106,7 @@ const emits = defineEmits<{
|
|
|
105
106
|
saveFilter: [value: SavedFilter];
|
|
106
107
|
filterInput: [{ filterKey: string; value: any; e: InputEvent }];
|
|
107
108
|
filterChange: [{ filterKey: string; value: any }];
|
|
109
|
+
reorder: [items: unknown[]];
|
|
108
110
|
}>();
|
|
109
111
|
|
|
110
112
|
const selectedItems: Ref<unknown[]> = defineModel('selectedItems', {
|
|
@@ -124,7 +126,8 @@ const getFilteredItems = () => {
|
|
|
124
126
|
? (item: unknown) => props.customSearch!(item, search.value)
|
|
125
127
|
: (item: unknown) => bmsDefaultSearchFilterFunction(item, search.value);
|
|
126
128
|
|
|
127
|
-
|
|
129
|
+
const filtered = filterItems(props.items).filter(applySearch);
|
|
130
|
+
return props.draggable ? filtered : sortItems(filtered);
|
|
128
131
|
};
|
|
129
132
|
|
|
130
133
|
const isMounting = ref(true);
|
|
@@ -167,12 +170,27 @@ watch(route, () => {
|
|
|
167
170
|
}
|
|
168
171
|
});
|
|
169
172
|
|
|
173
|
+
const pendingSelectionIndices = ref<number[] | null>(null);
|
|
174
|
+
|
|
175
|
+
const onSelectionIndicesUpdate = (indices: number[]) => {
|
|
176
|
+
pendingSelectionIndices.value = indices;
|
|
177
|
+
};
|
|
178
|
+
|
|
170
179
|
watch(
|
|
171
180
|
() => props.items,
|
|
172
181
|
() => {
|
|
173
182
|
if (!isMounting.value) {
|
|
174
183
|
items.value = getFilteredItems();
|
|
175
|
-
|
|
184
|
+
if (!props.draggable) {
|
|
185
|
+
selectedItems.value = [];
|
|
186
|
+
} else if (pendingSelectionIndices.value !== null) {
|
|
187
|
+
selectedItems.value = pendingSelectionIndices.value
|
|
188
|
+
.map((i) => items.value[i])
|
|
189
|
+
.filter(Boolean);
|
|
190
|
+
pendingSelectionIndices.value = null;
|
|
191
|
+
} else {
|
|
192
|
+
selectedItems.value = [];
|
|
193
|
+
}
|
|
176
194
|
}
|
|
177
195
|
},
|
|
178
196
|
);
|
|
@@ -233,12 +251,15 @@ const isTableSmall = computed(() => props.mode === TableMode.SMALL);
|
|
|
233
251
|
<UiBmsTable
|
|
234
252
|
v-model:selectedItems="selectedItems"
|
|
235
253
|
:loading="loading"
|
|
236
|
-
:items="currentItems"
|
|
254
|
+
:items="draggable ? items : currentItems"
|
|
237
255
|
:headers="headers"
|
|
238
256
|
:mode="mode as TableMode"
|
|
239
257
|
:hasFilters="filters.length > 0"
|
|
240
258
|
:sort="sort"
|
|
241
259
|
:selectable="selectable"
|
|
260
|
+
:draggable="draggable"
|
|
261
|
+
@reorder="emits('reorder', $event)"
|
|
262
|
+
@selectionIndicesUpdate="onSelectionIndicesUpdate"
|
|
242
263
|
:selectableDisabled="selectableDisabled"
|
|
243
264
|
:totalSize="totalSize"
|
|
244
265
|
:selectMode="selectMode"
|
|
@@ -759,3 +759,43 @@ WithChildElements.args = {
|
|
|
759
759
|
hasFilters: true,
|
|
760
760
|
totalSize: 2,
|
|
761
761
|
};
|
|
762
|
+
|
|
763
|
+
const players = [
|
|
764
|
+
{ name: 'Maignan', position: 'Gardien' },
|
|
765
|
+
{ name: 'Pavard', position: 'Défenseur' },
|
|
766
|
+
{ name: 'Upamecano', position: 'Défenseur' },
|
|
767
|
+
{ name: 'Hernandez', position: 'Défenseur' },
|
|
768
|
+
{ name: 'Camavinga', position: 'Milieu' },
|
|
769
|
+
{ name: 'Tchouameni', position: 'Milieu' },
|
|
770
|
+
{ name: 'Griezmann', position: 'Milieu' },
|
|
771
|
+
{ name: 'Mbappé', position: 'Attaquant' },
|
|
772
|
+
];
|
|
773
|
+
|
|
774
|
+
const draggableHeaders = [
|
|
775
|
+
{ key: 'name', label: 'Joueur' },
|
|
776
|
+
{ key: 'position', label: 'Poste' },
|
|
777
|
+
];
|
|
778
|
+
|
|
779
|
+
export const Draggable = () => ({
|
|
780
|
+
components: { UiBmsTable },
|
|
781
|
+
setup() {
|
|
782
|
+
const items = ref([...players]);
|
|
783
|
+
const selectedItems = ref([]);
|
|
784
|
+
const onReorder = (reordered) => { items.value = reordered; };
|
|
785
|
+
return { items, selectedItems, onReorder, draggableHeaders };
|
|
786
|
+
},
|
|
787
|
+
template: `
|
|
788
|
+
<div>
|
|
789
|
+
<UiBmsTable
|
|
790
|
+
v-model:selectedItems="selectedItems"
|
|
791
|
+
:items="items"
|
|
792
|
+
:headers="draggableHeaders"
|
|
793
|
+
:total-size="items.length"
|
|
794
|
+
draggable
|
|
795
|
+
@reorder="onReorder"
|
|
796
|
+
/>
|
|
797
|
+
<pre style="margin-top: 1rem; font-size: 0.75rem; color: #666;">Ordre : {{ items.map(i => i.name).join(' → ') }}</pre>
|
|
798
|
+
</div>
|
|
799
|
+
`,
|
|
800
|
+
});
|
|
801
|
+
Draggable.storyName = 'Draggable (réordonnancement)';
|
|
@@ -43,6 +43,7 @@ interface UiBmsTableProps {
|
|
|
43
43
|
totalSize: number;
|
|
44
44
|
maxSelectedSize?: number;
|
|
45
45
|
selectMode?: SelectMode;
|
|
46
|
+
draggable?: boolean;
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
const props = withDefaults(defineProps<UiBmsTableProps>(), {
|
|
@@ -66,8 +67,51 @@ const emits = defineEmits<{
|
|
|
66
67
|
clickHeader: [header: TableHeader];
|
|
67
68
|
selectAll: [];
|
|
68
69
|
clearSelection: [];
|
|
70
|
+
reorder: [items: unknown[]];
|
|
71
|
+
selectionIndicesUpdate: [indices: number[]];
|
|
69
72
|
}>();
|
|
70
73
|
|
|
74
|
+
// Drag & drop
|
|
75
|
+
const dragSourceIndex = ref<number | null>(null);
|
|
76
|
+
const dragTargetIndex = ref<number | null>(null);
|
|
77
|
+
const isDraggingDown = computed(
|
|
78
|
+
() =>
|
|
79
|
+
dragSourceIndex.value !== null &&
|
|
80
|
+
dragTargetIndex.value !== null &&
|
|
81
|
+
dragSourceIndex.value < dragTargetIndex.value,
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
const onRowDragStart = (index: number) => {
|
|
85
|
+
dragSourceIndex.value = index;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const onRowDragOver = (index: number) => {
|
|
89
|
+
dragTargetIndex.value = index;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const onRowDragEnd = () => {
|
|
93
|
+
if (
|
|
94
|
+
dragSourceIndex.value !== null &&
|
|
95
|
+
dragTargetIndex.value !== null &&
|
|
96
|
+
dragSourceIndex.value !== dragTargetIndex.value
|
|
97
|
+
) {
|
|
98
|
+
const reordered = [...props.items];
|
|
99
|
+
const [moved] = reordered.splice(dragSourceIndex.value, 1);
|
|
100
|
+
reordered.splice(dragTargetIndex.value, 0, moved);
|
|
101
|
+
|
|
102
|
+
if (selectedItems.value.length > 0) {
|
|
103
|
+
const newIndices = reordered
|
|
104
|
+
.map((item, i) => (selectedItems.value.includes(item) ? i : -1))
|
|
105
|
+
.filter((i) => i !== -1);
|
|
106
|
+
emits('selectionIndicesUpdate', newIndices);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
emits('reorder', reordered);
|
|
110
|
+
}
|
|
111
|
+
dragSourceIndex.value = null;
|
|
112
|
+
dragTargetIndex.value = null;
|
|
113
|
+
};
|
|
114
|
+
|
|
71
115
|
// Pagination
|
|
72
116
|
const pagination = ref<HTMLInputElement | null>(null);
|
|
73
117
|
const isFocusOnPagination = () =>
|
|
@@ -290,6 +334,7 @@ onMounted(() => {
|
|
|
290
334
|
>
|
|
291
335
|
<thead ref="thead" class="bms-table__header">
|
|
292
336
|
<tr class="bms-table__headers bms-table__row">
|
|
337
|
+
<th v-if="draggable" class="drag-handle-header"></th>
|
|
293
338
|
<th v-if="selectable">
|
|
294
339
|
<UiBmsInputCheckbox
|
|
295
340
|
v-if="selectMode !== SelectMode.SINGLE"
|
|
@@ -304,9 +349,9 @@ onMounted(() => {
|
|
|
304
349
|
:style="{
|
|
305
350
|
'--table-cell-width': header?.width || undefined,
|
|
306
351
|
}"
|
|
307
|
-
:class="getHeaderClasses(header, sort)"
|
|
352
|
+
:class="getHeaderClasses(header, draggable ? { key: null, value: SortValue.default } : sort)"
|
|
308
353
|
:key="header.label"
|
|
309
|
-
@click="emits('clickHeader', header)"
|
|
354
|
+
@click="!draggable && emits('clickHeader', header)"
|
|
310
355
|
>
|
|
311
356
|
<span class="header-content">
|
|
312
357
|
{{ header.label }}
|
|
@@ -323,7 +368,7 @@ onMounted(() => {
|
|
|
323
368
|
</span>
|
|
324
369
|
</BmsTooltip>
|
|
325
370
|
<component
|
|
326
|
-
v-if="header.sortable"
|
|
371
|
+
v-if="header.sortable && !draggable"
|
|
327
372
|
:is="getSortComponent(header)"
|
|
328
373
|
:size="18"
|
|
329
374
|
class="header-content-sort"
|
|
@@ -334,7 +379,7 @@ onMounted(() => {
|
|
|
334
379
|
</thead>
|
|
335
380
|
<tbody class="bms-table__body">
|
|
336
381
|
<template v-if="items.length">
|
|
337
|
-
<template v-for="item in items" :key="item">
|
|
382
|
+
<template v-for="(item, index) in items" :key="item">
|
|
338
383
|
<UiBmsTableRow
|
|
339
384
|
:item="item"
|
|
340
385
|
:selected-items="selectedItems"
|
|
@@ -343,7 +388,16 @@ onMounted(() => {
|
|
|
343
388
|
:select-mode="selectMode"
|
|
344
389
|
:selectable-disabled="selectableDisabled"
|
|
345
390
|
:dense="mode === TableMode.DENSE || mode === TableMode.SMALL"
|
|
391
|
+
:draggable="draggable"
|
|
392
|
+
:class="{
|
|
393
|
+
'bms-table__row--drag-insert-after': draggable && dragTargetIndex === index && dragSourceIndex !== index && isDraggingDown,
|
|
394
|
+
'bms-table__row--drag-insert-before': draggable && dragTargetIndex === index && dragSourceIndex !== index && !isDraggingDown,
|
|
395
|
+
}"
|
|
346
396
|
@select="onItemSelect"
|
|
397
|
+
@drag-start="onRowDragStart(index)"
|
|
398
|
+
@drag-over="onRowDragOver(index)"
|
|
399
|
+
@drag-end="onRowDragEnd"
|
|
400
|
+
@drop="onRowDragEnd"
|
|
347
401
|
>
|
|
348
402
|
<template v-for="cell in headers" v-slot:[cell.key]="slotData">
|
|
349
403
|
<slot :name="cell.key" v-bind="slotData" />
|
|
@@ -362,6 +416,7 @@ onMounted(() => {
|
|
|
362
416
|
:headers="filteredHeaders"
|
|
363
417
|
:select-mode="selectMode"
|
|
364
418
|
:selectable-disabled="selectableDisabled"
|
|
419
|
+
:draggable="draggable"
|
|
365
420
|
@select="onItemSelect"
|
|
366
421
|
>
|
|
367
422
|
<template
|
|
@@ -379,9 +434,9 @@ onMounted(() => {
|
|
|
379
434
|
<tr class="bms-table__row">
|
|
380
435
|
<td
|
|
381
436
|
:colspan="
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
437
|
+
filteredHeaders.length +
|
|
438
|
+
(selectable ? 1 : 0) +
|
|
439
|
+
(draggable ? 1 : 0)
|
|
385
440
|
"
|
|
386
441
|
class="bms-table__cell bms-table__cell--empty"
|
|
387
442
|
>
|
|
@@ -563,6 +618,18 @@ onMounted(() => {
|
|
|
563
618
|
padding-top: 16px;
|
|
564
619
|
}
|
|
565
620
|
|
|
621
|
+
.drag-handle-header {
|
|
622
|
+
width: 2em;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
:deep(.bms-table__row--drag-insert-before td) {
|
|
626
|
+
border-top: 2px solid var(--bms-main-100);
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
:deep(.bms-table__row--drag-insert-after td) {
|
|
630
|
+
border-bottom: 2px solid var(--bms-main-100);
|
|
631
|
+
}
|
|
632
|
+
|
|
566
633
|
.blob {
|
|
567
634
|
visibility: hidden;
|
|
568
635
|
--table-blob-height: 80px;
|
|
@@ -6,7 +6,20 @@
|
|
|
6
6
|
'bms-table__row--disabled': isChildElement,
|
|
7
7
|
'bms-table__row--dense': dense,
|
|
8
8
|
}"
|
|
9
|
+
:draggable="draggable && gripped"
|
|
10
|
+
@dragstart="emits('dragStart')"
|
|
11
|
+
@dragover="draggable && ($event.preventDefault(), emits('dragOver'))"
|
|
12
|
+
@dragend="gripped = false; emits('dragEnd')"
|
|
13
|
+
@drop="draggable && ($event.preventDefault(), emits('drop'))"
|
|
9
14
|
>
|
|
15
|
+
<td
|
|
16
|
+
v-if="draggable"
|
|
17
|
+
class="bms-table__row__cell--drag-handle"
|
|
18
|
+
@mousedown="!isChildElement && (gripped = true)"
|
|
19
|
+
@mouseup="gripped = false"
|
|
20
|
+
>
|
|
21
|
+
<GripVertical v-if="!isChildElement" class="drag-handle-icon" />
|
|
22
|
+
</td>
|
|
10
23
|
<td v-if="selectable" class="bms-table__row__cell__checkbox">
|
|
11
24
|
<BmsTooltip
|
|
12
25
|
:direction="TooltipDirection.Right"
|
|
@@ -76,10 +89,10 @@ import _isEqual from 'lodash/isEqual';
|
|
|
76
89
|
import _get from 'lodash/get';
|
|
77
90
|
import UiBmsInputCheckbox from '../form/UiBmsInputCheckbox.vue';
|
|
78
91
|
import BmsTooltip from '../feedback/BmsTooltip.vue';
|
|
79
|
-
import { CornerDownRight } from 'lucide-vue-next';
|
|
92
|
+
import { CornerDownRight, GripVertical } from 'lucide-vue-next';
|
|
80
93
|
import UiBmsTableCell from './UiBmsTableCell.vue';
|
|
81
94
|
import BmsInputRadio from '../form/BmsInputRadio.vue';
|
|
82
|
-
import { computed } from 'vue';
|
|
95
|
+
import { computed, ref } from 'vue';
|
|
83
96
|
|
|
84
97
|
interface Props {
|
|
85
98
|
item: any;
|
|
@@ -90,13 +103,23 @@ interface Props {
|
|
|
90
103
|
selectableDisabled?: boolean;
|
|
91
104
|
dense?: boolean;
|
|
92
105
|
isChildElement?: boolean;
|
|
106
|
+
draggable?: boolean;
|
|
93
107
|
}
|
|
94
108
|
const props = withDefaults(defineProps<Props>(), {
|
|
95
109
|
dense: false,
|
|
96
110
|
selectableDisabled: false,
|
|
111
|
+
draggable: false,
|
|
97
112
|
});
|
|
98
113
|
|
|
99
|
-
const
|
|
114
|
+
const gripped = ref(false);
|
|
115
|
+
|
|
116
|
+
const emits = defineEmits<{
|
|
117
|
+
select: [item: any];
|
|
118
|
+
dragStart: [];
|
|
119
|
+
dragOver: [];
|
|
120
|
+
dragEnd: [];
|
|
121
|
+
drop: [];
|
|
122
|
+
}>();
|
|
100
123
|
|
|
101
124
|
const currentItem = computed(() =>
|
|
102
125
|
props.isChildElement ? props.item.childElement : props.item,
|
|
@@ -163,6 +186,21 @@ const getAlignClass = (header: TableHeader) => {
|
|
|
163
186
|
margin-right: 1em;
|
|
164
187
|
}
|
|
165
188
|
}
|
|
189
|
+
|
|
190
|
+
&--drag-handle {
|
|
191
|
+
width: 2em;
|
|
192
|
+
cursor: default;
|
|
193
|
+
|
|
194
|
+
.drag-handle-icon {
|
|
195
|
+
display: block;
|
|
196
|
+
cursor: grab;
|
|
197
|
+
color: var(--bms-grey-50);
|
|
198
|
+
|
|
199
|
+
&:active {
|
|
200
|
+
cursor: grabbing;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
166
204
|
}
|
|
167
205
|
}
|
|
168
206
|
</style>
|