@ouestfrance/sipa-bms-ui 8.15.1 → 8.16.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/index.d.ts +3 -1
- package/dist/mockServiceWorker.js +1 -1
- package/dist/sipa-bms-ui.css +120 -34
- package/dist/sipa-bms-ui.es.js +4958 -3996
- package/dist/sipa-bms-ui.es.js.map +1 -1
- package/dist/sipa-bms-ui.umd.js +4965 -4001
- 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/index.ts +6 -0
- package/src/showroom/pages/autocomplete.vue +2 -2
- package/src/showroom/pages/server-table.vue +1 -3
- package/src/showroom/server.js +19 -11
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import {
|
|
3
|
+
computed,
|
|
4
|
+
onBeforeUnmount,
|
|
5
|
+
onMounted,
|
|
6
|
+
ref,
|
|
7
|
+
useTemplateRef,
|
|
8
|
+
watch,
|
|
9
|
+
} from 'vue';
|
|
10
|
+
|
|
11
|
+
const DEFAULT_MIN = 0;
|
|
12
|
+
const DEFAULT_MAX = 100;
|
|
13
|
+
|
|
14
|
+
interface Props {
|
|
15
|
+
splitOrientation?: 'horizontal' | 'vertical';
|
|
16
|
+
min?: number;
|
|
17
|
+
max?: number;
|
|
18
|
+
primary?: 'first' | 'second';
|
|
19
|
+
collapsed?: boolean;
|
|
20
|
+
ariaLabel?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
24
|
+
splitOrientation: 'vertical',
|
|
25
|
+
primary: 'first',
|
|
26
|
+
min: DEFAULT_MIN,
|
|
27
|
+
max: DEFAULT_MAX,
|
|
28
|
+
collapsable: false,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const emit = defineEmits(['update:collapsed']);
|
|
32
|
+
|
|
33
|
+
const split = defineModel<number>({
|
|
34
|
+
default: 50,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const container = useTemplateRef('split-window');
|
|
38
|
+
const primaryId = ref<string>(crypto.randomUUID());
|
|
39
|
+
|
|
40
|
+
//State Machine
|
|
41
|
+
const isDragging = ref<boolean>(false);
|
|
42
|
+
const startSplit = ref<number | null>(null);
|
|
43
|
+
const startPosition = ref<number | null>(null);
|
|
44
|
+
|
|
45
|
+
const min = computed(() =>
|
|
46
|
+
clamp(props.min ?? DEFAULT_MIN, DEFAULT_MIN, DEFAULT_MAX),
|
|
47
|
+
);
|
|
48
|
+
const max = computed(() =>
|
|
49
|
+
clamp(props.max ?? DEFAULT_MAX, DEFAULT_MIN, DEFAULT_MAX),
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
// Correction: état local pour collapsed
|
|
53
|
+
const collapsedLocal = ref(props.collapsed ?? false);
|
|
54
|
+
|
|
55
|
+
watch(
|
|
56
|
+
() => props.collapsed,
|
|
57
|
+
(val) => {
|
|
58
|
+
collapsedLocal.value = val ?? false;
|
|
59
|
+
},
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
function setCollapsed(val: boolean) {
|
|
63
|
+
collapsedLocal.value = val;
|
|
64
|
+
emit('update:collapsed', val);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const clampSplit = computed(() => {
|
|
68
|
+
if (collapsedLocal.value) {
|
|
69
|
+
return props.primary === 'first' ? min.value : max.value;
|
|
70
|
+
} else {
|
|
71
|
+
return clamp(split.value, min.value, max.value);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const size = computed(() => {
|
|
76
|
+
return `${clampSplit.value}fr auto ${100 - clampSplit.value}fr`;
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const gridStyle = computed(() => {
|
|
80
|
+
return props.splitOrientation === 'horizontal'
|
|
81
|
+
? {
|
|
82
|
+
gridTemplateRows: size.value,
|
|
83
|
+
gridTemplateColumns: 'none',
|
|
84
|
+
}
|
|
85
|
+
: {
|
|
86
|
+
gridTemplateColumns: size.value,
|
|
87
|
+
gridTemplateRows: 'none',
|
|
88
|
+
};
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const HANDLED_KEYS = [
|
|
92
|
+
'ArrowLeft',
|
|
93
|
+
'ArrowRight',
|
|
94
|
+
'ArrowUp',
|
|
95
|
+
'ArrowDown',
|
|
96
|
+
'Enter',
|
|
97
|
+
'Home',
|
|
98
|
+
'End',
|
|
99
|
+
];
|
|
100
|
+
|
|
101
|
+
onMounted(() => {
|
|
102
|
+
window.addEventListener('pointermove', onPointerMove);
|
|
103
|
+
window.addEventListener('pointerup', onPointerUp);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
onBeforeUnmount(() => {
|
|
107
|
+
window.removeEventListener('pointermove', onPointerMove);
|
|
108
|
+
window.removeEventListener('pointerup', onPointerUp);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
function onPointerDown(evt: PointerEvent) {
|
|
112
|
+
isDragging.value = true;
|
|
113
|
+
setCollapsed(false);
|
|
114
|
+
startSplit.value = clampSplit.value;
|
|
115
|
+
startPosition.value =
|
|
116
|
+
props.splitOrientation === 'vertical' ? evt.clientX : evt.clientY;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function onPointerMove(evt: PointerEvent) {
|
|
120
|
+
if (!isDragging.value) return;
|
|
121
|
+
_move(evt);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function onPointerUp(evt: PointerEvent) {
|
|
125
|
+
if (!isDragging.value) return;
|
|
126
|
+
_move(evt);
|
|
127
|
+
isDragging.value = false;
|
|
128
|
+
startPosition.value = null;
|
|
129
|
+
startSplit.value = null;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function onKeyDown(evt: KeyboardEvent) {
|
|
133
|
+
if (
|
|
134
|
+
!HANDLED_KEYS.includes(evt.key) ||
|
|
135
|
+
(props.splitOrientation === 'horizontal' &&
|
|
136
|
+
(evt.key === 'ArrowLeft' || evt.key === 'ArrowRight')) ||
|
|
137
|
+
(props.splitOrientation === 'vertical' &&
|
|
138
|
+
(evt.key === 'ArrowUp' || evt.key === 'ArrowDown'))
|
|
139
|
+
) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
evt.preventDefault();
|
|
144
|
+
evt.stopPropagation();
|
|
145
|
+
|
|
146
|
+
switch (evt.key) {
|
|
147
|
+
case 'ArrowLeft':
|
|
148
|
+
case 'ArrowUp':
|
|
149
|
+
if (collapsedLocal.value === true) {
|
|
150
|
+
setCollapsed(false);
|
|
151
|
+
}
|
|
152
|
+
split.value = Math.max(min.value, clampSplit.value - 1);
|
|
153
|
+
break;
|
|
154
|
+
case 'ArrowRight':
|
|
155
|
+
case 'ArrowDown':
|
|
156
|
+
if (collapsedLocal.value === true) {
|
|
157
|
+
setCollapsed(false);
|
|
158
|
+
}
|
|
159
|
+
split.value = Math.min(max.value, clampSplit.value + 1);
|
|
160
|
+
break;
|
|
161
|
+
case 'Enter':
|
|
162
|
+
setCollapsed(!collapsedLocal.value);
|
|
163
|
+
break;
|
|
164
|
+
case 'Home':
|
|
165
|
+
if (collapsedLocal.value === true) {
|
|
166
|
+
setCollapsed(false);
|
|
167
|
+
}
|
|
168
|
+
split.value = props.primary === 'first' ? min.value : max.value;
|
|
169
|
+
break;
|
|
170
|
+
case 'End':
|
|
171
|
+
if (collapsedLocal.value === true) {
|
|
172
|
+
setCollapsed(false);
|
|
173
|
+
}
|
|
174
|
+
split.value = props.primary === 'first' ? max.value : min.value;
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function _move(evt: PointerEvent) {
|
|
180
|
+
if (startPosition.value === null || startSplit.value === null) return;
|
|
181
|
+
|
|
182
|
+
const currentPosition =
|
|
183
|
+
props.splitOrientation === 'vertical' ? evt.clientX : evt.clientY;
|
|
184
|
+
|
|
185
|
+
const delta = currentPosition - startPosition.value;
|
|
186
|
+
|
|
187
|
+
const containerSize =
|
|
188
|
+
props.splitOrientation === 'vertical'
|
|
189
|
+
? container.value?.getBoundingClientRect().width || 1
|
|
190
|
+
: container.value?.getBoundingClientRect().height || 1;
|
|
191
|
+
|
|
192
|
+
const deltaPercent = (delta / containerSize) * 100;
|
|
193
|
+
split.value = startSplit.value + deltaPercent;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function clamp(value: number, minValue: number, maxValue: number) {
|
|
197
|
+
return Math.min(Math.max(value, minValue), maxValue);
|
|
198
|
+
}
|
|
199
|
+
</script>
|
|
200
|
+
|
|
201
|
+
<template>
|
|
202
|
+
<div
|
|
203
|
+
ref="split-window"
|
|
204
|
+
class="split-window"
|
|
205
|
+
:class="`split-window--${splitOrientation}`"
|
|
206
|
+
:style="gridStyle"
|
|
207
|
+
>
|
|
208
|
+
<div
|
|
209
|
+
class="split-window__first-pane"
|
|
210
|
+
:id="primary === 'first' ? primaryId : undefined"
|
|
211
|
+
>
|
|
212
|
+
<slot name="first" />
|
|
213
|
+
</div>
|
|
214
|
+
<div
|
|
215
|
+
class="split-window__separator"
|
|
216
|
+
role="separator"
|
|
217
|
+
tabindex="0"
|
|
218
|
+
:aria-label="props.ariaLabel || 'Séparateur de volet'"
|
|
219
|
+
:aria-valuemin="min"
|
|
220
|
+
:aria-valuemax="max"
|
|
221
|
+
:aria-valuenow="clampSplit"
|
|
222
|
+
:aria-orientation="splitOrientation"
|
|
223
|
+
:aria-controls="primaryId"
|
|
224
|
+
@pointerdown.prevent.stop="onPointerDown"
|
|
225
|
+
@keydown="onKeyDown"
|
|
226
|
+
/>
|
|
227
|
+
<div
|
|
228
|
+
class="split-window__second-pane"
|
|
229
|
+
:id="primary === 'second' ? primaryId : undefined"
|
|
230
|
+
>
|
|
231
|
+
<slot name="second" />
|
|
232
|
+
</div>
|
|
233
|
+
</div>
|
|
234
|
+
</template>
|
|
235
|
+
|
|
236
|
+
<style scoped lang="scss">
|
|
237
|
+
.split-window {
|
|
238
|
+
display: grid;
|
|
239
|
+
width: 100%;
|
|
240
|
+
height: 100%;
|
|
241
|
+
|
|
242
|
+
&__separator {
|
|
243
|
+
position: relative;
|
|
244
|
+
z-index: 2;
|
|
245
|
+
|
|
246
|
+
&:before {
|
|
247
|
+
content: '';
|
|
248
|
+
position: absolute;
|
|
249
|
+
top: 0;
|
|
250
|
+
left: 0;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
&:focus-within {
|
|
254
|
+
&:before {
|
|
255
|
+
outline: 2px solid black;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
&--vertical {
|
|
261
|
+
.split-window__separator {
|
|
262
|
+
height: 100%;
|
|
263
|
+
width: 0;
|
|
264
|
+
|
|
265
|
+
&:before {
|
|
266
|
+
content: '';
|
|
267
|
+
width: 8px;
|
|
268
|
+
height: 100%;
|
|
269
|
+
transform: translate(-50%, 0);
|
|
270
|
+
cursor: col-resize;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
&--horizontal {
|
|
276
|
+
.split-window__separator {
|
|
277
|
+
height: 0;
|
|
278
|
+
width: 100%;
|
|
279
|
+
|
|
280
|
+
&:before {
|
|
281
|
+
content: '';
|
|
282
|
+
width: 100%;
|
|
283
|
+
height: 8px;
|
|
284
|
+
transform: translate(0, -50%);
|
|
285
|
+
cursor: row-resize;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
</style>
|
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>
|
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) => {
|