@ouestfrance/sipa-bms-ui 8.15.1 → 8.17.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/layout/BmsFloatingWindow.vue.d.ts +19 -0
- package/dist/components/layout/BmsSplitWindow.vue.d.ts +35 -0
- package/dist/components/table/BmsTable.vue.d.ts +3 -1
- package/dist/components/table/UiBmsTable.vue.d.ts +1 -1
- package/dist/helpers/table.helper.d.ts +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/mockServiceWorker.js +1 -1
- package/dist/models/table.model.d.ts +2 -1
- package/dist/sipa-bms-ui.css +168 -88
- package/dist/sipa-bms-ui.es.js +5086 -4115
- package/dist/sipa-bms-ui.es.js.map +1 -1
- package/dist/sipa-bms-ui.umd.js +5094 -4120
- package/dist/sipa-bms-ui.umd.js.map +1 -1
- package/package.json +19 -19
- package/src/components/form/BmsMultiSelect.vue +5 -1
- package/src/components/form/BmsServerAutocomplete.vue +13 -3
- package/src/components/layout/BmsFloatingWindow.stories.js +109 -0
- package/src/components/layout/BmsFloatingWindow.vue +83 -0
- package/src/components/layout/BmsModal.vue +1 -1
- package/src/components/layout/BmsSplitWindow.stories.js +155 -0
- package/src/components/layout/BmsSplitWindow.vue +290 -0
- package/src/components/table/BmsTable.stories.js +47 -0
- package/src/components/table/BmsTable.vue +22 -3
- package/src/components/table/UiBmsTable.spec.ts +47 -3
- package/src/components/table/UiBmsTable.stories.js +43 -0
- package/src/components/table/UiBmsTable.vue +51 -91
- package/src/components/table/UiBmsTableRow.vue +18 -3
- package/src/documentation/principles.mdx +73 -7
- package/src/helpers/table.helper.ts +19 -0
- package/src/index.ts +6 -0
- package/src/models/table.model.ts +1 -0
- package/src/showroom/pages/autocomplete.vue +2 -2
- package/src/showroom/pages/server-table.vue +2 -7
- package/src/showroom/pages/table.vue +3 -0
- package/src/showroom/server.js +19 -11
|
@@ -21,12 +21,12 @@ import BmsLoader from '../feedback/BmsLoader.vue';
|
|
|
21
21
|
import { ChevronDown, ChevronsUpDown, ChevronUp } from 'lucide-vue-next';
|
|
22
22
|
import UiBmsInputCheckbox from '../form/UiBmsInputCheckbox.vue';
|
|
23
23
|
import BmsAlert from '../feedback/BmsAlert.vue';
|
|
24
|
-
import { enforceActionsColumnHeader } from '@/helpers';
|
|
24
|
+
import { enforceActionsColumnHeader, getHeaderClasses } from '@/helpers';
|
|
25
25
|
import UiBmsTableRow from './UiBmsTableRow.vue';
|
|
26
26
|
|
|
27
27
|
interface UiBmsTableProps {
|
|
28
28
|
headers: TableHeader[];
|
|
29
|
-
items:
|
|
29
|
+
items: any[];
|
|
30
30
|
mode?: 'normal' | 'dense';
|
|
31
31
|
loading?: boolean;
|
|
32
32
|
hasFilters?: boolean;
|
|
@@ -62,37 +62,31 @@ const emits = defineEmits<{
|
|
|
62
62
|
clearSelection: [];
|
|
63
63
|
}>();
|
|
64
64
|
|
|
65
|
+
// Pagination
|
|
65
66
|
const pagination = ref<HTMLInputElement | null>(null);
|
|
66
|
-
const
|
|
67
|
-
|
|
67
|
+
const isFocusOnPagination = () =>
|
|
68
|
+
pagination.value?.contains(document.activeElement);
|
|
68
69
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
watch(
|
|
71
|
+
() => props.items,
|
|
72
|
+
async function keepFocusOnPaginationWhenUsed(newVal, oldVal) {
|
|
73
|
+
if (oldVal.length > 0 && newVal.length > 0 && isFocusOnPagination()) {
|
|
74
|
+
await nextTick();
|
|
75
|
+
pagination.value?.focus();
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
{ deep: true },
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
// Headers style
|
|
82
|
+
const filteredHeaders = computed(() =>
|
|
83
|
+
enforceActionsColumnHeader(props.headers),
|
|
84
|
+
);
|
|
72
85
|
|
|
73
86
|
const tableClass = computed(
|
|
74
87
|
() => `bms-table__table bms-table__table--${props.mode}`,
|
|
75
88
|
);
|
|
76
89
|
|
|
77
|
-
const getAlignClass = (header: TableHeader) => {
|
|
78
|
-
const align = !header.align ? 'start' : header.align;
|
|
79
|
-
return `u-text-align-${align}`;
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
const getHeaderClasses = (header: TableHeader): string[] => {
|
|
83
|
-
const classes = [getAlignClass(header), 'bms-table__header-cell'];
|
|
84
|
-
if (header.class) {
|
|
85
|
-
classes.push(header.class);
|
|
86
|
-
}
|
|
87
|
-
if (header.sortable) {
|
|
88
|
-
classes.push('sortable');
|
|
89
|
-
}
|
|
90
|
-
if (props.sort.key === header.key) {
|
|
91
|
-
classes.push('sorted');
|
|
92
|
-
}
|
|
93
|
-
return classes;
|
|
94
|
-
};
|
|
95
|
-
|
|
96
90
|
const getSortComponent = (header: TableHeader): Component => {
|
|
97
91
|
if (!props.sort.key || header.key !== props.sort.key) {
|
|
98
92
|
return ChevronsUpDown;
|
|
@@ -107,48 +101,23 @@ const getSortComponent = (header: TableHeader): Component => {
|
|
|
107
101
|
}
|
|
108
102
|
};
|
|
109
103
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
watch(
|
|
115
|
-
() => props.items,
|
|
116
|
-
async (newVal, oldVal) => {
|
|
117
|
-
if (oldVal.length > 0 && newVal.length > 0 && isFocusOnPagination()) {
|
|
118
|
-
await nextTick();
|
|
119
|
-
pagination.value?.focus();
|
|
120
|
-
}
|
|
121
|
-
},
|
|
122
|
-
{ deep: true },
|
|
123
|
-
);
|
|
124
|
-
|
|
125
|
-
onscroll = () => {
|
|
126
|
-
if (thead.value) {
|
|
127
|
-
const { top: theadTop } = thead.value.getBoundingClientRect();
|
|
128
|
-
const headers = document.getElementsByTagName('header');
|
|
129
|
-
if (headers.length > 0) {
|
|
130
|
-
const header = headers[0];
|
|
131
|
-
const { height: headerHeight } = header.getBoundingClientRect();
|
|
132
|
-
isHeaderStuck.value = headerHeight === theadTop;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
const isItemSelected = (item: unknown): boolean => {
|
|
138
|
-
return (
|
|
139
|
-
props.selectMode === SelectMode.ALL ||
|
|
140
|
-
!!selectedItems.value.find((it) => _isEqual(item, it))
|
|
141
|
-
);
|
|
142
|
-
};
|
|
104
|
+
// Selection
|
|
105
|
+
const isItemSelected = (item: unknown): boolean =>
|
|
106
|
+
props.selectMode === SelectMode.ALL ||
|
|
107
|
+
!!selectedItems.value.find((it) => _isEqual(item, it));
|
|
143
108
|
|
|
144
109
|
const onItemSelect = (item: unknown) => {
|
|
145
|
-
if (
|
|
146
|
-
selectedItems.value =
|
|
147
|
-
(it) => !_isEqual(item, it),
|
|
148
|
-
);
|
|
149
|
-
areAllCurrentItemsSelected.value = false;
|
|
110
|
+
if (props.selectMode === SelectMode.SINGLE) {
|
|
111
|
+
selectedItems.value = [item];
|
|
150
112
|
} else {
|
|
151
|
-
|
|
113
|
+
if (isItemSelected(item)) {
|
|
114
|
+
selectedItems.value = selectedItems.value.filter(
|
|
115
|
+
(it) => !_isEqual(item, it),
|
|
116
|
+
);
|
|
117
|
+
areAllCurrentItemsSelected.value = false;
|
|
118
|
+
} else {
|
|
119
|
+
selectedItems.value.push(item);
|
|
120
|
+
}
|
|
152
121
|
}
|
|
153
122
|
};
|
|
154
123
|
|
|
@@ -165,26 +134,27 @@ const onToggleSelectAllCurrentItems = () => {
|
|
|
165
134
|
}
|
|
166
135
|
|
|
167
136
|
// If we have a selection spanning across more than the current page
|
|
168
|
-
if (selectedItems.value.length > props.
|
|
137
|
+
if (selectedItems.value.length > props.totalSize) {
|
|
169
138
|
selectedItems.value = [] as unknown[];
|
|
170
139
|
} else {
|
|
171
|
-
// If
|
|
140
|
+
// If only some are selected
|
|
172
141
|
if (!areAllCurrentItemsSelected.value) {
|
|
173
142
|
props.items.forEach((item) => {
|
|
174
143
|
if (!isItemSelected(item)) {
|
|
175
144
|
selectedItems.value.push(item);
|
|
176
145
|
}
|
|
146
|
+
if (item?.childElement && !isItemSelected(item?.childElement)) {
|
|
147
|
+
selectedItems.value.push(item.childElement);
|
|
148
|
+
}
|
|
177
149
|
});
|
|
178
150
|
} else {
|
|
179
|
-
// If
|
|
180
|
-
selectedItems.value =
|
|
181
|
-
(selectedItem) =>
|
|
182
|
-
!props.items.find((item) => _isEqual(item, selectedItem)),
|
|
183
|
-
);
|
|
151
|
+
// If all the current page is selected
|
|
152
|
+
selectedItems.value = [];
|
|
184
153
|
}
|
|
185
154
|
}
|
|
186
155
|
};
|
|
187
156
|
|
|
157
|
+
// Blob animation
|
|
188
158
|
const blob = ref<HTMLDivElement | null>(null);
|
|
189
159
|
const mainComponent = ref<HTMLDivElement | null>(null);
|
|
190
160
|
|
|
@@ -220,6 +190,7 @@ const onMouseMove = (e: MouseEvent) => {
|
|
|
220
190
|
};
|
|
221
191
|
|
|
222
192
|
window.addEventListener('mousemove', onMouseMove);
|
|
193
|
+
|
|
223
194
|
watch(
|
|
224
195
|
() => props.items.length,
|
|
225
196
|
() => {
|
|
@@ -282,7 +253,9 @@ onMounted(() => {
|
|
|
282
253
|
<span
|
|
283
254
|
class="select-mode-all"
|
|
284
255
|
@click="selectAll"
|
|
285
|
-
v-if="
|
|
256
|
+
v-if="
|
|
257
|
+
totalSize < maxSelectedSize && selectMode !== SelectMode.SINGLE
|
|
258
|
+
"
|
|
286
259
|
>
|
|
287
260
|
Sélectionner la totalité des {{ totalSize }} éléments
|
|
288
261
|
</span>
|
|
@@ -305,14 +278,11 @@ onMounted(() => {
|
|
|
305
278
|
:class="tableClass"
|
|
306
279
|
ref="mainComponent"
|
|
307
280
|
>
|
|
308
|
-
<thead
|
|
309
|
-
ref="thead"
|
|
310
|
-
class="bms-table__header"
|
|
311
|
-
:class="{ stuck: isHeaderStuck }"
|
|
312
|
-
>
|
|
281
|
+
<thead ref="thead" class="bms-table__header">
|
|
313
282
|
<tr class="bms-table__headers bms-table__row">
|
|
314
283
|
<th v-if="selectable">
|
|
315
284
|
<UiBmsInputCheckbox
|
|
285
|
+
v-if="selectMode !== SelectMode.SINGLE"
|
|
316
286
|
name="select-all"
|
|
317
287
|
:disabled="items.length === 0 || selectableDisabled"
|
|
318
288
|
@update:model-value="onToggleSelectAllCurrentItems"
|
|
@@ -324,7 +294,7 @@ onMounted(() => {
|
|
|
324
294
|
:style="{
|
|
325
295
|
'--table-cell-width': header?.width || undefined,
|
|
326
296
|
}"
|
|
327
|
-
:class="getHeaderClasses(header)"
|
|
297
|
+
:class="getHeaderClasses(header, sort)"
|
|
328
298
|
:key="header.label"
|
|
329
299
|
@click="emits('clickHeader', header)"
|
|
330
300
|
>
|
|
@@ -360,8 +330,7 @@ onMounted(() => {
|
|
|
360
330
|
<slot name="default" :row="row"></slot>
|
|
361
331
|
</template>
|
|
362
332
|
</UiBmsTableRow>
|
|
363
|
-
|
|
364
|
-
<template v-if="(item as any)?.childElement">
|
|
333
|
+
<template v-if="item?.childElement">
|
|
365
334
|
<slot name="child-element">
|
|
366
335
|
<UiBmsTableRow
|
|
367
336
|
is-child-element
|
|
@@ -371,6 +340,7 @@ onMounted(() => {
|
|
|
371
340
|
:headers="filteredHeaders"
|
|
372
341
|
:select-mode="selectMode"
|
|
373
342
|
:selectable-disabled="selectableDisabled"
|
|
343
|
+
@select="onItemSelect"
|
|
374
344
|
>
|
|
375
345
|
<template
|
|
376
346
|
v-for="cell in headers"
|
|
@@ -579,16 +549,6 @@ onMounted(() => {
|
|
|
579
549
|
border-top-left-radius: var(--table-cell-radius);
|
|
580
550
|
}
|
|
581
551
|
|
|
582
|
-
.stuck {
|
|
583
|
-
th:first-child {
|
|
584
|
-
border-top-left-radius: 0;
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
th:last-child {
|
|
588
|
-
border-top-right-radius: 0;
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
|
|
592
552
|
th:last-child {
|
|
593
553
|
border-top-right-radius: var(--table-cell-radius);
|
|
594
554
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<tr
|
|
3
3
|
class="bms-table__row"
|
|
4
4
|
:class="{
|
|
5
|
-
'bms-table__row--selected': isItemSelected(
|
|
5
|
+
'bms-table__row--selected': isItemSelected(currentItem),
|
|
6
6
|
'bms-table__row--disabled': isChildElement,
|
|
7
7
|
'bms-table__row--dense': dense,
|
|
8
8
|
}"
|
|
@@ -13,11 +13,20 @@
|
|
|
13
13
|
tooltip-text="Vous ne pouvez pas désélectionner un élément unitairement si vous avez choisi de sélectionner la totalité des éléments"
|
|
14
14
|
:activated="selectMode === SelectMode.ALL"
|
|
15
15
|
>
|
|
16
|
+
<BmsInputRadio
|
|
17
|
+
v-if="selectMode === SelectMode.SINGLE"
|
|
18
|
+
:name="uuid()"
|
|
19
|
+
:disabled="selectableDisabled"
|
|
20
|
+
:value="currentItem"
|
|
21
|
+
:model-value="isItemSelected(currentItem) ? currentItem : null"
|
|
22
|
+
@update:model-value="emits('select', currentItem)"
|
|
23
|
+
/>
|
|
16
24
|
<UiBmsInputCheckbox
|
|
25
|
+
v-else
|
|
17
26
|
:name="uuid()"
|
|
18
27
|
:disabled="selectMode === SelectMode.ALL || selectableDisabled"
|
|
19
|
-
:model-value="isItemSelected(
|
|
20
|
-
@update:model-value="emits('select',
|
|
28
|
+
:model-value="isItemSelected(currentItem)"
|
|
29
|
+
@update:model-value="emits('select', currentItem)"
|
|
21
30
|
/>
|
|
22
31
|
</BmsTooltip>
|
|
23
32
|
</td>
|
|
@@ -69,6 +78,8 @@ import UiBmsInputCheckbox from '../form/UiBmsInputCheckbox.vue';
|
|
|
69
78
|
import BmsTooltip from '../feedback/BmsTooltip.vue';
|
|
70
79
|
import { CornerDownRight } from 'lucide-vue-next';
|
|
71
80
|
import UiBmsTableCell from './UiBmsTableCell.vue';
|
|
81
|
+
import BmsInputRadio from '../form/BmsInputRadio.vue';
|
|
82
|
+
import { computed } from 'vue';
|
|
72
83
|
|
|
73
84
|
interface Props {
|
|
74
85
|
item: any;
|
|
@@ -87,6 +98,10 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
87
98
|
|
|
88
99
|
const emits = defineEmits<{ select: [item: any] }>();
|
|
89
100
|
|
|
101
|
+
const currentItem = computed(() =>
|
|
102
|
+
props.isChildElement ? props.item.childElement : props.item,
|
|
103
|
+
);
|
|
104
|
+
|
|
90
105
|
const isItemSelected = (item: unknown): boolean => {
|
|
91
106
|
return (
|
|
92
107
|
props.selectMode === SelectMode.ALL ||
|
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Canvas,
|
|
3
|
-
Meta,
|
|
4
|
-
Story,
|
|
5
|
-
Stories,
|
|
6
|
-
Controls,
|
|
7
|
-
} from '@storybook/addon-docs/blocks';
|
|
1
|
+
import { Meta } from '@storybook/addon-docs/blocks';
|
|
8
2
|
|
|
9
3
|

|
|
10
4
|
|
|
@@ -21,3 +15,75 @@ bms UI focuses on the essentials, we concentrate on what is useful for interface
|
|
|
21
15
|
## Design System by constraint
|
|
22
16
|
|
|
23
17
|
A constraint-based design system is designed to offer a consistent user experience across all products that use it. By limiting possible interpretations, this type of design system provides a clear and consistent logic that facilitates understanding and use of the products. Constraints can be applied to visual elements such as colors, typography, and font sizes, as well as interactive elements such as buttons and menus. By using a constraint-based design system, users can interact with products with confidence, knowing that the design elements will be consistent and predictable.
|
|
18
|
+
|
|
19
|
+
## 📖 Documentation of the **bms UI** Design System
|
|
20
|
+
|
|
21
|
+
> **Mission** – Provide a **single Design System** dedicated to the Sipa group’s Back‑Office tools, limiting the use of the Vue framework and offering a constraint‑driven approach to interface design and construction. Goal: reduce costs, ensure visual and functional consistency, and simplify maintenance.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
### 1️⃣ Context & Objectives
|
|
26
|
+
|
|
27
|
+
- **Who?**
|
|
28
|
+
The bms UI Design System is actually maintained by the frontend team of **bms**.
|
|
29
|
+
|
|
30
|
+
- **Why?**
|
|
31
|
+
- Uniform the look‑and‑feel of internal products and applications.
|
|
32
|
+
- Limit Vue code sprawl and avoid ad‑hoc solutions.
|
|
33
|
+
- Speed up development with reusable, tested components.
|
|
34
|
+
|
|
35
|
+
- **Target audience**
|
|
36
|
+
Front‑end developers, designers, QA engineers, project managers, and anyone involved in creating or maintaining Back‑Office interfaces.
|
|
37
|
+
|
|
38
|
+
### 2️⃣ Core Principles
|
|
39
|
+
|
|
40
|
+
#### 2.1 Focused – Essentials First
|
|
41
|
+
|
|
42
|
+
> _“We concentrate on what truly matters for interfaces, nothing superfluous.”_
|
|
43
|
+
|
|
44
|
+
- **Functional minimalism** – Each component must solve a clearly identified need.
|
|
45
|
+
- **Dependency pruning** – Keep external imports to a minimum to reduce bundle size.
|
|
46
|
+
- **Concise documentation** – Every component includes essential props, available slots, and a usage example.
|
|
47
|
+
|
|
48
|
+
#### 2.2 Design System by Constraint – Constraint as a Cohesion Lever
|
|
49
|
+
|
|
50
|
+
> _“By limiting possible interpretations, we create a clear and predictable logic.”_
|
|
51
|
+
|
|
52
|
+
| Domain | Applied Constraints |
|
|
53
|
+
| ---------------- | ---------------------------------------------------------- |
|
|
54
|
+
| **Colors** | Restricted palette – no free‑form colors. |
|
|
55
|
+
| **Typography** | One font families, predefined sizes. |
|
|
56
|
+
| **Spacing** | Margin/padding scale based on a 8 px step. |
|
|
57
|
+
| **Interactions** | Standardised states (default, hover, focus, disabled). |
|
|
58
|
+
| **Behavior** | Uniform Vue API (`v-model` where applicable, named slots). |
|
|
59
|
+
|
|
60
|
+
These constraints are **declarative** – they’re baked directly into components (e.g., `mode="danger"` to have a red button), preventing stylistic drift.
|
|
61
|
+
|
|
62
|
+
### 3️⃣ Project Architecture
|
|
63
|
+
|
|
64
|
+
- **Single export** – `import BmsButton from '../components/button/BmsButton.vue';`
|
|
65
|
+
- **Integrated Storybook** – Each component has a `*.stories.js` file showcasing the UI and the props & slots for each component.```
|
|
66
|
+
- **Typescript components** - Each component as typed props & events, and the relevant types are exported by the library (e.g. TableHeader, Caption, etc.)
|
|
67
|
+
|
|
68
|
+
### 4️⃣ Usage Guide
|
|
69
|
+
|
|
70
|
+
#### 4.1 Installation
|
|
71
|
+
|
|
72
|
+
Gitlab documentation https://gitlab.ouest-france.fr/sipa-ouest-france/platform/platform-library-vuejs-bms#installation
|
|
73
|
+
|
|
74
|
+
### 5️⃣ Quick FAQ
|
|
75
|
+
|
|
76
|
+
- **Can I add a custom color?**
|
|
77
|
+
**Answer:** No – all colors must come from the defined palette. If a legitimate need arises, open a **Jira** ticket.
|
|
78
|
+
|
|
79
|
+
- **How do I handle a new breakpoint?**
|
|
80
|
+
**Answer:** There are no breakpoints – bmsUI only provides desktop‑only interfaces.
|
|
81
|
+
|
|
82
|
+
- **Does the design system support dark mode?**
|
|
83
|
+
**Answer:** No – theming is not available at this time.
|
|
84
|
+
|
|
85
|
+
### 6️⃣ Contact & Support
|
|
86
|
+
|
|
87
|
+
- **Teams**: [#🎨bmsUI](https://teams.microsoft.com/l/channel/19%3Au9dPXr-JjoRoaLat-EyS-QEKit1ZzUwQ7G0VrzvDkTE1%40thread.tacv2/%F0%9F%8E%A8bmsUI?groupId=677fd2f9-de86-4bf9-a00c-71817f033ad3&tenantId=a59e9cc9-4ed4-43c4-9f1e-ca78d44b0072&ngc=true&allowXTenantAccess=)
|
|
88
|
+
- **Issues**: [#Jira] (⚠️ WIP)
|
|
89
|
+
- **Gitlab**: [Gitlab](https://gitlab.ouest-france.fr/sipa-ouest-france/platform/platform-library-vuejs-bms)
|
|
@@ -86,3 +86,22 @@ export const enforceActionsColumnHeader = (
|
|
|
86
86
|
.filter((h) => !h?.action)
|
|
87
87
|
.concat(actionsHeaders.length ? actionsHeaders[0] : []);
|
|
88
88
|
};
|
|
89
|
+
|
|
90
|
+
const getAlignClass = (header: TableHeader) => {
|
|
91
|
+
const align = !header.align ? 'start' : header.align;
|
|
92
|
+
return `u-text-align-${align}`;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export const getHeaderClasses = (header: TableHeader, sort: Sort): string[] => {
|
|
96
|
+
const classes = [getAlignClass(header), 'bms-table__header-cell'];
|
|
97
|
+
if (header.class) {
|
|
98
|
+
classes.push(header.class);
|
|
99
|
+
}
|
|
100
|
+
if (header.sortable) {
|
|
101
|
+
classes.push('sortable');
|
|
102
|
+
}
|
|
103
|
+
if (sort.key === header.key) {
|
|
104
|
+
classes.push('sorted');
|
|
105
|
+
}
|
|
106
|
+
return classes;
|
|
107
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -37,12 +37,14 @@ import BmsTextArea from './components/form/BmsTextArea.vue';
|
|
|
37
37
|
|
|
38
38
|
import BmsContentPageLayout from './components/layout/BmsContentPageLayout.vue';
|
|
39
39
|
import BmsCard from './components/layout/BmsCard.vue';
|
|
40
|
+
import BmsFloatingWindow from './components/layout/BmsFloatingWindow.vue';
|
|
40
41
|
import BmsForm from './components/layout/BmsForm.vue';
|
|
41
42
|
import BmsHeader from './components/layout/BmsHeader.vue';
|
|
42
43
|
import BmsHeaderTitle from './components/layout/BmsHeaderTitle.vue';
|
|
43
44
|
import BmsModal from './components/layout/BmsModal.vue';
|
|
44
45
|
import BmsOverlay from './components/layout/BmsOverlay.vue';
|
|
45
46
|
import BmsSection from './components/layout/BmsSection.vue';
|
|
47
|
+
import BmsSplitWindow from './components/layout/BmsSplitWindow.vue';
|
|
46
48
|
import BmsStep from './components/layout/BmsStep.vue';
|
|
47
49
|
import BmsStepper from './components/layout/BmsStepper.vue';
|
|
48
50
|
|
|
@@ -108,12 +110,14 @@ export const createBmsUi = () => ({
|
|
|
108
110
|
|
|
109
111
|
app.component('BmsContentPageLayout', BmsContentPageLayout);
|
|
110
112
|
app.component('BmsCard', BmsCard);
|
|
113
|
+
app.component('BmsFloatingWindow', BmsFloatingWindow);
|
|
111
114
|
app.component('BmsForm', BmsForm);
|
|
112
115
|
app.component('BmsHeader', BmsHeader);
|
|
113
116
|
app.component('BmsHeaderTitle', BmsHeaderTitle);
|
|
114
117
|
app.component('BmsModal', BmsModal);
|
|
115
118
|
app.component('BmsOverlay', BmsOverlay);
|
|
116
119
|
app.component('BmsSection', BmsSection);
|
|
120
|
+
app.component('BmsSplitWindow', BmsSplitWindow);
|
|
117
121
|
app.component('BmsStep', BmsStep);
|
|
118
122
|
app.component('BmsStepper', BmsStepper);
|
|
119
123
|
|
|
@@ -185,12 +189,14 @@ export {
|
|
|
185
189
|
BmsTextArea,
|
|
186
190
|
BmsContentPageLayout,
|
|
187
191
|
BmsCard,
|
|
192
|
+
BmsFloatingWindow,
|
|
188
193
|
BmsForm,
|
|
189
194
|
BmsHeader,
|
|
190
195
|
BmsHeaderTitle,
|
|
191
196
|
BmsModal,
|
|
192
197
|
BmsOverlay,
|
|
193
198
|
BmsSection,
|
|
199
|
+
BmsSplitWindow,
|
|
194
200
|
BmsStep,
|
|
195
201
|
BmsStepper,
|
|
196
202
|
BmsBackButton,
|
|
@@ -99,10 +99,10 @@ const onAddNewOption = (newOption: string) => {
|
|
|
99
99
|
};
|
|
100
100
|
|
|
101
101
|
const autocompleteRequest = (
|
|
102
|
-
|
|
102
|
+
_abortController: AbortController,
|
|
103
103
|
searchString?: string,
|
|
104
104
|
) =>
|
|
105
105
|
fetch(`http://localhost:3000/pokemon-types?search=${searchString}`).then(
|
|
106
|
-
(res) => res.json(),
|
|
106
|
+
(res) => res.json().then((res) => ({ data: res })),
|
|
107
107
|
);
|
|
108
108
|
</script>
|
|
@@ -30,10 +30,7 @@
|
|
|
30
30
|
<div>Nb Selected items: {{ selectedItems.length }}</div>
|
|
31
31
|
<div>
|
|
32
32
|
Selected items:
|
|
33
|
-
{{
|
|
34
|
-
selectMode === SelectMode.ALL &&
|
|
35
|
-
selectedItems?.map((i: any) => '#' + i.id + ' : ' + i.name).join(', ')
|
|
36
|
-
}}
|
|
33
|
+
{{ selectedItems?.map((i: any) => '#' + i.id + ' : ' + i.name).join(', ') }}
|
|
37
34
|
</div>
|
|
38
35
|
</template>
|
|
39
36
|
|
|
@@ -56,9 +53,7 @@ const pokemonTypeAutocompleteRequest = async (
|
|
|
56
53
|
signal: abortController.signal,
|
|
57
54
|
});
|
|
58
55
|
|
|
59
|
-
return {
|
|
60
|
-
data,
|
|
61
|
-
};
|
|
56
|
+
return { data };
|
|
62
57
|
};
|
|
63
58
|
|
|
64
59
|
const filters: Filter[] = [
|
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
help-link="/help-path"
|
|
14
14
|
activity-link="/activity-path"
|
|
15
15
|
v-model:selectedItems="selectedItems"
|
|
16
|
+
selectable
|
|
17
|
+
:select-mode="'single'"
|
|
16
18
|
@saveFilter="onSaveFilter"
|
|
17
19
|
@deleteSavedFilter="onDeleteSavedFilter"
|
|
18
20
|
@filterInput="onFilterInput"
|
|
@@ -58,6 +60,7 @@ import {
|
|
|
58
60
|
Filter,
|
|
59
61
|
InputType,
|
|
60
62
|
SavedFilter,
|
|
63
|
+
SelectMode,
|
|
61
64
|
SortValue,
|
|
62
65
|
StatusType,
|
|
63
66
|
} from '@/models';
|
package/src/showroom/server.js
CHANGED
|
@@ -9,17 +9,25 @@ app.use(cors());
|
|
|
9
9
|
const port = 3000;
|
|
10
10
|
|
|
11
11
|
app.get('/pokemon-types', (req, res) => {
|
|
12
|
-
|
|
13
|
-
label:
|
|
14
|
-
value:
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
12
|
+
res.send([
|
|
13
|
+
{ label: 'Grass', value: 'Grass' },
|
|
14
|
+
{ label: 'Poison', value: 'Poison' },
|
|
15
|
+
{ label: 'Fire', value: 'Fire' },
|
|
16
|
+
{ label: 'Flying', value: 'Flying' },
|
|
17
|
+
{ label: 'Water', value: 'Water' },
|
|
18
|
+
{ label: 'Bug', value: 'Bug' },
|
|
19
|
+
{ label: 'Normal', value: 'Normal' },
|
|
20
|
+
{ label: 'Electric', value: 'Electric' },
|
|
21
|
+
{ label: 'Ground', value: 'Ground' },
|
|
22
|
+
{ label: 'Fairy', value: 'Fairy' },
|
|
23
|
+
{ label: 'Fighting', value: 'Fighting' },
|
|
24
|
+
{ label: 'Psychic', value: 'Psychic' },
|
|
25
|
+
{ label: 'Rock', value: 'Rock' },
|
|
26
|
+
{ label: 'Steel', value: 'Steel' },
|
|
27
|
+
{ label: 'Ice', value: 'Ice' },
|
|
28
|
+
{ label: 'Ghost', value: 'Ghost' },
|
|
29
|
+
{ label: 'Dragon', value: 'Dragon' },
|
|
30
|
+
]);
|
|
23
31
|
});
|
|
24
32
|
|
|
25
33
|
app.get('/pokemons', (req, res) => {
|