@dative-gpi/foundation-shared-components 1.0.161 → 1.0.162

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.
@@ -5,6 +5,7 @@
5
5
  :required="$props.required"
6
6
  :disabled="$props.disabled"
7
7
  :label="$props.label"
8
+ :maxWidth="$props.maxWidth"
8
9
  >
9
10
  <FSRow
10
11
  align="bottom-center"
@@ -23,15 +24,12 @@
23
24
  padding="0 0 2px 0"
24
25
  align="center-center"
25
26
  >
26
- <FSCol
27
- width="fill"
28
- >
27
+ <FSCol>
29
28
  <FSSlider
30
29
  minWidth='min(300px, 90vw)'
31
30
  :disabled="$props.disabled"
32
31
  :color="ColorEnum.Light"
33
32
  :thumbColor="ColorEnum.Primary"
34
- :thumbSize="18"
35
33
  :trackSize="8"
36
34
  thumb-label="always"
37
35
  :step="$props.stepTime"
@@ -52,20 +50,14 @@
52
50
  </FSSpan>
53
51
  </template>
54
52
  <template
55
- #tick-label="{ tick, index }"
53
+ #tick-label="{ tick }"
56
54
  >
57
- <FSRow
58
- v-if="index % Math.trunc(ticks.length / maximumTickToShow) === 0 || ticks.length < maximumTickToShow"
59
- >
55
+ <FSRow>
60
56
  <FSText
61
57
  :color="lightColors.dark"
62
58
  font="text-overline"
63
59
  >
64
- {{ intervalTime <= 3600000
65
- ?
66
- epochToShortTimeOnlyFormat(tick.value)
67
- :
68
- epochToDayMonthShortOnly(tick.value)
60
+ {{ tickPrecision === TimePrecision.Hours ? epochToShortTimeOnlyFormat(tick.value) : epochToDayMonthShortOnly(tick.value)
69
61
  }}
70
62
  </FSText>
71
63
  </FSRow>
@@ -88,18 +80,19 @@
88
80
  <script lang="ts">
89
81
  import { computed, defineComponent, ref, watch } from "vue";
90
82
 
83
+ import { useBreakpoints, useColors } from '@dative-gpi/foundation-shared-components/composables';
91
84
  import { useDateFormat, useDateExpression } from "@dative-gpi/foundation-shared-services/composables";
92
85
 
93
86
  import { ColorEnum } from "@dative-gpi/foundation-shared-components/models";
94
- import { useBreakpoints, useColors } from '@dative-gpi/foundation-shared-components/composables';
87
+ import { computeTicks, TimePrecision } from '@dative-gpi/foundation-shared-components/utils';
95
88
 
96
89
  import FSCol from '@dative-gpi/foundation-shared-components/components/FSCol.vue';
97
90
  import FSSpan from '@dative-gpi/foundation-shared-components/components/FSSpan.vue';
98
91
  import FSText from '@dative-gpi/foundation-shared-components/components/FSText.vue';
99
92
  import FSSlider from '@dative-gpi/foundation-shared-components/components/FSSlider.vue';
93
+ import FSPlayButtons from '@dative-gpi/foundation-shared-components/components/FSPlayButtons.vue';
100
94
  import FSBaseField from '@dative-gpi/foundation-shared-components/components/fields/FSBaseField.vue';
101
95
  import FSTermField from '@dative-gpi/foundation-shared-components/components/fields/FSTermField.vue';
102
- import FSPlayButtons from '@dative-gpi/foundation-shared-components/components/FSPlayButtons.vue';
103
96
 
104
97
  export default defineComponent({
105
98
  name: "FSInstantPicker",
@@ -110,7 +103,7 @@ export default defineComponent({
110
103
  FSSlider,
111
104
  FSTermField,
112
105
  FSBaseField,
113
- FSPlayButtons
106
+ FSPlayButtons,
114
107
  },
115
108
  props: {
116
109
  label: {
@@ -119,8 +112,7 @@ export default defineComponent({
119
112
  },
120
113
  modelValue: {
121
114
  type: Number,
122
- required: false,
123
- default: 0,
115
+ required: false
124
116
  },
125
117
  startDate: {
126
118
  type: String,
@@ -164,6 +156,11 @@ export default defineComponent({
164
156
  type: Number,
165
157
  required: false,
166
158
  default: 50
159
+ },
160
+ maxWidth: {
161
+ type: String as PropType<string | null>,
162
+ required: false,
163
+ default: null
167
164
  }
168
165
  },
169
166
  emits: ['update:modelValue', 'update:startDate', 'update:endDate'],
@@ -180,38 +177,26 @@ export default defineComponent({
180
177
  const startTimestamp = computed(() => convertTermToEpoch(props.startDate));
181
178
  const endTimestamp = computed(() => convertTermToEpoch(props.endDate));
182
179
 
183
- const intervalTime = computed(() => {
184
- const possibleIntervals = [60000, 3600000, 86400000];
185
-
186
- const interval = possibleIntervals.find(interval => {
187
- return (endTimestamp.value - startTimestamp.value) / interval < 100;
188
- });
189
-
190
- if (interval) {
191
- return interval;
192
- }
193
- return 86400000;
180
+ const tickCount = computed(() => {
181
+ if (isExtraSmall.value) { return 3; }
182
+ if (isMobileSized.value) { return 4; }
183
+ return 5;
194
184
  });
195
185
 
196
- const ticks = computed(() => {
197
- const ticks: number[] = [];
198
-
199
- const firstTick = Math.ceil(startTimestamp.value / intervalTime.value) * intervalTime.value;
200
- for (let i = firstTick; i <= endTimestamp.value; i += intervalTime.value) {
201
- ticks.push(i);
202
- }
203
- return ticks;
186
+ // Pour la précision, on peut rester sur l'heure ou le jour selon la plage
187
+ const tickPrecision = computed(() => {
188
+ const rangeDuration = endTimestamp.value - startTimestamp.value;
189
+ if (rangeDuration <= 86400000 * tickCount.value) { return TimePrecision.Hours; }
190
+ if (rangeDuration <= 2592000000 * tickCount.value) { return TimePrecision.Days; }
191
+ return TimePrecision.Months;
204
192
  });
205
193
 
206
- const maximumTickToShow = computed(() => {
207
- if (isMobileSized.value) {
208
- return 5;
209
- }
210
- if (isExtraSmall.value) {
211
- return 4;
212
- }
213
- return 6;
214
- });
194
+ const ticks = computed(() => computeTicks({
195
+ start: startTimestamp.value,
196
+ end: endTimestamp.value,
197
+ tickCount: tickCount.value,
198
+ precision: tickPrecision.value
199
+ }));
215
200
 
216
201
  const onPlayingChange = (value: boolean) => {
217
202
  playing.value = value;
@@ -225,8 +210,8 @@ export default defineComponent({
225
210
  emit('update:modelValue', endTimestamp.value);
226
211
  };
227
212
 
228
- watch(() => [props.startDate, props.endDate], () => {
229
- if(props.modelValue < startTimestamp.value || props.modelValue > endTimestamp.value) {
213
+ watch([() => props.startDate, () => props.endDate, () => props.modelValue], () => {
214
+ if(!props.modelValue || props.modelValue < startTimestamp.value || props.modelValue > endTimestamp.value) {
230
215
  emit('update:modelValue', endTimestamp.value);
231
216
  }
232
217
  }, { immediate: true });
@@ -236,7 +221,7 @@ export default defineComponent({
236
221
  clearInterval(playingInterval.value);
237
222
  } else {
238
223
  playingInterval.value = setInterval(() => {
239
- if(props.modelValue + props.stepTime <= endTimestamp.value) {
224
+ if(props.modelValue && props.modelValue + props.stepTime <= endTimestamp.value) {
240
225
  emit('update:modelValue', props.modelValue + props.stepTime);
241
226
  } else {
242
227
  emit('update:modelValue', endTimestamp.value);
@@ -251,10 +236,10 @@ export default defineComponent({
251
236
  playing,
252
237
  ColorEnum,
253
238
  lightColors,
254
- intervalTime,
255
239
  endTimestamp,
240
+ TimePrecision,
241
+ tickPrecision,
256
242
  startTimestamp,
257
- maximumTickToShow,
258
243
  epochToISO,
259
244
  onPlayingChange,
260
245
  onClickForward,
@@ -0,0 +1,165 @@
1
+ <template>
2
+ <FSMenu
3
+ :location="$props.location"
4
+ :closeOnContentClick="true"
5
+ :contained="false"
6
+ minWidth="0"
7
+ v-model="modelValue"
8
+ v-bind="$attrs"
9
+ >
10
+ <template
11
+ #activator="{ props }"
12
+ >
13
+ <slot
14
+ name="activator"
15
+ v-bind="props"
16
+ >
17
+ <FSButton
18
+ v-bind="props"
19
+ :icon="$props.icon"
20
+ :iconSize="$props.iconSize"
21
+ :color="$props.buttonColor"
22
+ :variant="$props.buttonVariant"
23
+ />
24
+ </slot>
25
+ </template>
26
+ <FSCard
27
+ :maxWidth="$props.maxWidth"
28
+ :width="$props.width"
29
+ padding="2px"
30
+ :border="false"
31
+ :elevation="true"
32
+ align="center-center"
33
+ >
34
+ <slot
35
+ name="content"
36
+ >
37
+ <FSCol
38
+ gap="0px"
39
+ >
40
+ <FSFadeOut
41
+ :scrollOutside="false"
42
+ maxHeight="80dvh"
43
+ >
44
+ <FSClickable
45
+ v-for="(item, index) in $props.items"
46
+ width="100%"
47
+ padding="8px"
48
+ height="40px"
49
+ :key="index"
50
+ :border="false"
51
+ @click="onClickItem(item)"
52
+ >
53
+ <slot
54
+ name="item"
55
+ v-bind="{ item, index }"
56
+ >
57
+ <FSRow
58
+ align="center-left"
59
+ >
60
+ <FSIcon
61
+ v-if="item.icon"
62
+ :icon="item.icon"
63
+ />
64
+ <FSText
65
+ font="text-body"
66
+ >
67
+ {{ item.label }}
68
+ </FSText>
69
+ </FSRow>
70
+ </slot>
71
+ </FSClickable>
72
+ </FSFadeOut>
73
+ </FSCol>
74
+ </slot>
75
+ </FSCard>
76
+ </FSMenu>
77
+ </template>
78
+
79
+ <script lang="ts">
80
+ import { defineComponent, ref, type PropType } from "vue";
81
+
82
+ import { useColors } from "@dative-gpi/foundation-shared-components/composables";
83
+
84
+ import { ColorEnum, type ColorBase } from '@dative-gpi/foundation-shared-components/models';
85
+
86
+ import FSRow from '@dative-gpi/foundation-shared-components/components/FSRow.vue';
87
+ import FSCol from '@dative-gpi/foundation-shared-components/components/FSCol.vue';
88
+ import FSIcon from '@dative-gpi/foundation-shared-components/components/FSIcon.vue';
89
+ import FSMenu from '@dative-gpi/foundation-shared-components/components/FSMenu.vue';
90
+ import FSCard from '@dative-gpi/foundation-shared-components/components/FSCard.vue';
91
+ import FSText from '@dative-gpi/foundation-shared-components/components/FSText.vue';
92
+ import FSButton from '@dative-gpi/foundation-shared-components/components/FSButton.vue';
93
+ import FSClickable from '@dative-gpi/foundation-shared-components/components/FSClickable.vue';
94
+
95
+ export default defineComponent({
96
+ name: "FSInformationsMenu",
97
+ components: {
98
+ FSMenu,
99
+ FSCard,
100
+ FSRow,
101
+ FSText,
102
+ FSButton,
103
+ FSCol,
104
+ FSIcon,
105
+ FSClickable
106
+ },
107
+ props: {
108
+ items: {
109
+ type: Array as PropType<{label: string, icon?: string, onClick: () => void, closeOnContentClick?: boolean}[]>,
110
+ default: () => []
111
+ },
112
+ location: {
113
+ type: String,
114
+ default: "bottom"
115
+ },
116
+ width: {
117
+ type: [Array, String, Number] as PropType<string[] | number[] | string | number | null>,
118
+ default: null
119
+ },
120
+ maxWidth: {
121
+ type: [Array, String, Number] as PropType<string[] | number[] | string | number | null>,
122
+ default: "90dvw"
123
+ },
124
+ icon: {
125
+ type: String,
126
+ default: "mdi-dots-horizontal"
127
+ },
128
+ iconSize: {
129
+ type: String,
130
+ default: "18px"
131
+ },
132
+ buttonColor: {
133
+ type: String as PropType<ColorBase>,
134
+ default: ColorEnum.Light
135
+ },
136
+ buttonVariant: {
137
+ type: String as PropType<"standard" | "full" | "icon">,
138
+ required: false,
139
+ default: "icon"
140
+ },
141
+ },
142
+ emits: ["update:modelValue"],
143
+ setup() {
144
+ const modelValue = ref(false);
145
+
146
+ const { getColors } = useColors();
147
+
148
+ const lightColors = getColors(ColorEnum.Light);
149
+
150
+ const onClickItem = (item: { onClick: () => void, closeOnContentClick?: boolean }) => {
151
+ item.onClick();
152
+ if (item.closeOnContentClick !== false) {
153
+ modelValue.value = false;
154
+ }
155
+ };
156
+
157
+ return {
158
+ ColorEnum,
159
+ modelValue,
160
+ lightColors,
161
+ onClickItem
162
+ };
163
+ }
164
+ });
165
+ </script>
@@ -0,0 +1,261 @@
1
+ <template>
2
+ <FSBaseField
3
+ :description="$props.description"
4
+ :hideHeader="$props.hideHeader"
5
+ :required="$props.required"
6
+ :disabled="$props.disabled"
7
+ :label="$props.label"
8
+ :maxWidth="$props.maxWidth"
9
+ >
10
+ <FSRow
11
+ align="bottom-center"
12
+ gap="32px"
13
+ >
14
+ <FSTermField
15
+ width="430px"
16
+ :label="$tr('ui.instant-picker.analyze-period', 'Analyze Period')"
17
+ :startDate="$props.startDate"
18
+ :endDate="$props.endDate"
19
+ :disabled="$props.disabled"
20
+ @update:startDate="$emit('update:startDate', $event)"
21
+ @update:endDate="$emit('update:endDate', $event)"
22
+ />
23
+ <FSRow
24
+ padding="0 0 2px 0"
25
+ align="center-center"
26
+ >
27
+ <FSCol>
28
+ <FSRangeSlider
29
+ minWidth='min(300px, 90vw)'
30
+ :disabled="$props.disabled"
31
+ :color="ColorEnum.Light"
32
+ :thumbColor="ColorEnum.Primary"
33
+ :trackFillColor="ColorEnum.Primary"
34
+ :trackSize="8"
35
+ thumb-label="always"
36
+ :step="$props.stepTime"
37
+ :min="startTimestamp"
38
+ :max="endTimestamp"
39
+ :ticks="ticks"
40
+ showTicks="always"
41
+ :modelValue="$props.modelValue"
42
+ @update:modelValue="$emit('update:modelValue', $event)"
43
+ >
44
+ <template
45
+ #thumb-label="{ modelValue }"
46
+ >
47
+ <FSSpan
48
+ font="text-overline"
49
+ >
50
+ {{ epochToMonthShortTimeFormat(modelValue) }}
51
+ </FSSpan>
52
+ </template>
53
+ <template
54
+ #tick-label="{ tick }"
55
+ >
56
+ <FSRow>
57
+ <FSText
58
+ :color="lightColors.dark"
59
+ font="text-overline"
60
+ >
61
+ {{ tickPrecision === TimePrecision.Hours ? epochToShortTimeOnlyFormat(tick.value) : epochToDayMonthShortOnly(tick.value) }}
62
+ </FSText>
63
+ </FSRow>
64
+ </template>
65
+ </FSRangeSlider>
66
+ </FSCol>
67
+ <FSPlayButtons
68
+ v-if="$props.playable"
69
+ :disabled="$props.disabled"
70
+ :modelValue="playing"
71
+ @click:backward="onClickBackward"
72
+ @click:forward="onClickForward"
73
+ @update:modelValue="onPlayingChange"
74
+ />
75
+ </FSRow>
76
+ </FSRow>
77
+ </FSBaseField>
78
+ </template>
79
+
80
+ <script lang="ts">
81
+ import { computed, defineComponent, ref, watch, type PropType } from "vue";
82
+
83
+ import { useBreakpoints, useColors } from '@dative-gpi/foundation-shared-components/composables';
84
+ import { useDateFormat, useDateExpression } from "@dative-gpi/foundation-shared-services/composables";
85
+
86
+ import { ColorEnum } from "@dative-gpi/foundation-shared-components/models";
87
+ import { computeTicks, TimePrecision } from '@dative-gpi/foundation-shared-components/utils';
88
+
89
+ import FSCol from '@dative-gpi/foundation-shared-components/components/FSCol.vue';
90
+ import FSSpan from '@dative-gpi/foundation-shared-components/components/FSSpan.vue';
91
+ import FSText from '@dative-gpi/foundation-shared-components/components/FSText.vue';
92
+ import FSPlayButtons from '@dative-gpi/foundation-shared-components/components/FSPlayButtons.vue';
93
+ import FSRangeSlider from '@dative-gpi/foundation-shared-components/components/FSRangeSlider.vue';
94
+ import FSBaseField from '@dative-gpi/foundation-shared-components/components/fields/FSBaseField.vue';
95
+ import FSTermField from '@dative-gpi/foundation-shared-components/components/fields/FSTermField.vue';
96
+
97
+ export default defineComponent({
98
+ name: "FSRangePicker",
99
+ components: {
100
+ FSCol,
101
+ FSSpan,
102
+ FSText,
103
+ FSTermField,
104
+ FSBaseField,
105
+ FSRangeSlider,
106
+ FSPlayButtons,
107
+ },
108
+ props: {
109
+ label: {
110
+ type: String,
111
+ required: false,
112
+ },
113
+ mode: {
114
+ type: String as () => 'single' | 'range',
115
+ required: false,
116
+ default: 'single'
117
+ },
118
+ modelValue: {
119
+ type: Object as () => [number, number],
120
+ required: true
121
+ },
122
+ startDate: {
123
+ type: String,
124
+ required: true
125
+ },
126
+ endDate: {
127
+ type: String,
128
+ required: true
129
+ },
130
+ description: {
131
+ type: String,
132
+ required: false,
133
+ default: null
134
+ },
135
+ hideHeader: {
136
+ type: Boolean,
137
+ required: false,
138
+ default: false
139
+ },
140
+ required: {
141
+ type: Boolean,
142
+ required: false,
143
+ default: false
144
+ },
145
+ disabled: {
146
+ type: Boolean,
147
+ required: false,
148
+ default: false
149
+ },
150
+ playable: {
151
+ type: Boolean,
152
+ required: false,
153
+ default: true
154
+ },
155
+ stepTime: {
156
+ type: Number,
157
+ required: false,
158
+ default: 60000
159
+ },
160
+ playingStepDuration: {
161
+ type: Number,
162
+ required: false,
163
+ default: 50
164
+ },
165
+ maxWidth: {
166
+ type: String as PropType<string | null>,
167
+ required: false,
168
+ default: null
169
+ }
170
+ },
171
+ emits: ['update:modelValue', 'update:startDate', 'update:endDate'],
172
+ setup(props, { emit }) {
173
+ const { epochToShortTimeOnlyFormat, epochToShortDateFormat, epochToDayMonthShortOnly, epochToISO, epochToMonthShortTimeFormat } = useDateFormat();
174
+ const { convert : convertTermToEpoch } = useDateExpression();
175
+ const { isMobileSized, isExtraSmall } = useBreakpoints();
176
+ const { getColors } = useColors();
177
+
178
+ const lightColors = getColors(ColorEnum.Light);
179
+ const playing = ref(false);
180
+ const playingInterval = ref();
181
+
182
+ const startTimestamp = computed(() => convertTermToEpoch(props.startDate));
183
+ const endTimestamp = computed(() => convertTermToEpoch(props.endDate));
184
+
185
+ const tickCount = computed(() => {
186
+ if (isExtraSmall.value) { return 3; }
187
+ if (isMobileSized.value) { return 4; }
188
+ return 5;
189
+ });
190
+
191
+ const tickPrecision = computed(() => {
192
+ const rangeDuration = endTimestamp.value - startTimestamp.value;
193
+ if (rangeDuration <= 86400000 * tickCount.value) { return TimePrecision.Hours; }
194
+ if (rangeDuration <= 2592000000 * tickCount.value) { return TimePrecision.Days; }
195
+ return TimePrecision.Months;
196
+ });
197
+
198
+ // Génération des ticks via la fonction utilitaire
199
+ const ticks = computed(() => computeTicks({
200
+ start: startTimestamp.value,
201
+ end: endTimestamp.value,
202
+ tickCount: tickCount.value,
203
+ precision: tickPrecision.value
204
+ }));
205
+
206
+ const onPlayingChange = (value: boolean) => {
207
+ playing.value = value;
208
+ };
209
+
210
+ const onClickBackward = () => {
211
+ const modelValueDuration = props.modelValue[1] - props.modelValue[0];
212
+ emit('update:modelValue', [startTimestamp.value, startTimestamp.value + modelValueDuration]);
213
+ };
214
+
215
+ const onClickForward = () => {
216
+ const modelValueDuration = props.modelValue[1] - props.modelValue[0];
217
+ emit('update:modelValue', [endTimestamp.value - modelValueDuration, endTimestamp.value]);
218
+ };
219
+
220
+ watch([() => props.startDate, () => props.endDate, () => props.modelValue], () => {
221
+ if((props.modelValue[0] < startTimestamp.value || props.modelValue[1] > endTimestamp.value)) {
222
+ emit('update:modelValue', [startTimestamp.value, endTimestamp.value]);
223
+ }
224
+ }, { immediate: true });
225
+
226
+ watch(playing, (value) => {
227
+ if(!value && playingInterval.value) {
228
+ clearInterval(playingInterval.value);
229
+ return;
230
+ }
231
+
232
+ playingInterval.value = setInterval(() => {
233
+ if(props.modelValue[0] + props.stepTime <= endTimestamp.value && props.modelValue[1] + props.stepTime <= endTimestamp.value) {
234
+ emit('update:modelValue', [props.modelValue[0] + props.stepTime, props.modelValue[1] + props.stepTime]);
235
+ } else {
236
+ playing.value = false;
237
+ }
238
+ }, props.playingStepDuration);
239
+ });
240
+
241
+ return {
242
+ ticks,
243
+ playing,
244
+ ColorEnum,
245
+ lightColors,
246
+ endTimestamp,
247
+ TimePrecision,
248
+ tickPrecision,
249
+ startTimestamp,
250
+ epochToISO,
251
+ onPlayingChange,
252
+ onClickForward,
253
+ onClickBackward,
254
+ epochToShortDateFormat,
255
+ epochToShortTimeOnlyFormat,
256
+ epochToDayMonthShortOnly,
257
+ epochToMonthShortTimeFormat
258
+ };
259
+ },
260
+ });
261
+ </script>
@@ -0,0 +1,84 @@
1
+ <template>
2
+ <FSBaseField
3
+ :label="$props.label"
4
+ :description="$props.description"
5
+ :required="$props.required"
6
+ :disabled="$props.disabled"
7
+ >
8
+ <FSRow>
9
+ <v-range-slider
10
+ hide-details
11
+ width="100%"
12
+ :disabled="$props.disabled"
13
+ :ripple="false"
14
+ :color="$props.color"
15
+ :trackSize="6"
16
+ :elevation="0"
17
+ :tickSize="4"
18
+ :modelValue="$props.modelValue ?? undefined"
19
+ @update:modelValue="$emit('update:modelValue', $event)"
20
+ v-bind="$attrs"
21
+ >
22
+ <template
23
+ v-for="(_, name) in $slots"
24
+ v-slot:[name]="slotData"
25
+ >
26
+ <slot
27
+ :name="name"
28
+ v-bind="slotData"
29
+ />
30
+ </template>
31
+ </v-range-slider>
32
+ </FSRow>
33
+ </FSBaseField>
34
+ </template>
35
+
36
+ <script lang="ts">
37
+ import { defineComponent, type PropType } from "vue";
38
+
39
+ import { type ColorBase, ColorEnum } from "@dative-gpi/foundation-shared-components/models";
40
+
41
+ import FSRow from '@dative-gpi/foundation-shared-components/components/FSRow.vue';
42
+ import FSBaseField from '@dative-gpi/foundation-shared-components/components/fields/FSBaseField.vue';
43
+
44
+ export default defineComponent({
45
+ name: "FSRangeSlider",
46
+ components: {
47
+ FSBaseField,
48
+ FSRow
49
+ },
50
+ props: {
51
+ label: {
52
+ type: String as PropType<string | null>,
53
+ required: false,
54
+ default: null
55
+ },
56
+ description: {
57
+ type: String as PropType<string | null>,
58
+ required: false,
59
+ default: null
60
+ },
61
+ modelValue: {
62
+ type: Object as PropType<[number, number] | null>,
63
+ required: false,
64
+ default: null
65
+ },
66
+ color: {
67
+ type: String as PropType<ColorBase>,
68
+ required: false,
69
+ default: ColorEnum.Dark
70
+ },
71
+ required: {
72
+ type: Boolean,
73
+ required: false,
74
+ default: false
75
+ },
76
+ disabled: {
77
+ type: Boolean,
78
+ required: false,
79
+ default: false
80
+ }
81
+ },
82
+ emits: ["update:modelValue"]
83
+ });
84
+ </script>
@@ -1,85 +1,55 @@
1
1
  <template>
2
- <FSCol>
3
- <slot
4
- name="label"
5
- >
6
- <FSRow
7
- :wrap="false"
2
+ <FSBaseField
3
+ :label="$props.label"
4
+ :description="$props.description"
5
+ :required="$props.required"
6
+ :disabled="$props.disabled"
7
+ :style="style"
8
+ >
9
+ <FSRow>
10
+ <v-slider
11
+ hide-details
12
+ class="fs-slider"
13
+ width="100%"
14
+ :color="$props.color"
15
+ :disabled="$props.disabled"
16
+ :ripple="false"
17
+ :trackSize="6"
18
+ :elevation="0"
19
+ :tickSize="4"
20
+ :modelValue="$props.modelValue ?? undefined"
21
+ @update:modelValue="$emit('update:modelValue', $event)"
22
+ v-bind="$attrs"
8
23
  >
9
- <FSSpan
10
- v-if="$props.label"
11
- class="fs-slider-label"
12
- font="text-overline"
13
- :style="style"
24
+ <template
25
+ v-for="(_, name) in $slots"
26
+ v-slot:[name]="slotData"
14
27
  >
15
- {{ $props.label }}
16
- </FSSpan>
17
- <FSSpan
18
- v-if="$props.label && $props.required"
19
- style="margin-left: -8px;"
20
- class="fs-slider-label"
21
- font="text-overline"
22
- :ellipsis="false"
23
- :style="style"
24
- >
25
- *
26
- </FSSpan>
27
- </FSRow>
28
- </slot>
29
- <v-slider
30
- class="fs-slider"
31
- hide-details
32
- :disabled="$props.disabled"
33
- :ripple="false"
34
- :style="style"
35
- :elevation="0"
36
- :tickSize="4"
37
- :modelValue="$props.modelValue ?? undefined"
38
- @update:modelValue="$emit('update:modelValue', $event)"
39
- v-bind="$attrs"
40
- >
41
- <template
42
- v-for="(_, name) in $slots"
43
- v-slot:[name]="slotData"
44
- >
45
- <slot
46
- :name="name"
47
- v-bind="slotData"
48
- />
49
- </template>
50
- </v-slider>
51
- <slot
52
- name="description"
53
- >
54
- <FSSpan
55
- v-if="$props.description"
56
- class="fs-slider-description"
57
- font="text-overline"
58
- :lineClamp="2"
59
- :style="style"
60
- >
61
- {{ $props.description }}
62
- </FSSpan>
63
- </slot>
64
- </FSCol>
28
+ <slot
29
+ :name="name"
30
+ v-bind="slotData"
31
+ />
32
+ </template>
33
+ </v-slider>
34
+ </FSRow>
35
+ </FSBaseField>
65
36
  </template>
66
37
 
67
38
  <script lang="ts">
68
39
  import { computed, defineComponent, type PropType, type StyleValue } from "vue";
69
40
 
41
+ import { useColors } from '@dative-gpi/foundation-shared-components/composables';
42
+
70
43
  import { type ColorBase, ColorEnum } from "@dative-gpi/foundation-shared-components/models";
71
- import { useColors } from "@dative-gpi/foundation-shared-components/composables";
72
44
 
73
- import FSSpan from "./FSSpan.vue";
74
- import FSCol from "./FSCol.vue";
75
- import FSRow from "./FSRow.vue";
45
+ import FSRow from '@dative-gpi/foundation-shared-components/components/FSRow.vue';
46
+ import FSBaseField from '@dative-gpi/foundation-shared-components/components/fields/FSBaseField.vue';
76
47
 
77
48
  export default defineComponent({
78
49
  name: "FSSlider",
79
50
  components: {
80
- FSSpan,
81
- FSCol,
82
- FSRow
51
+ FSRow,
52
+ FSBaseField
83
53
  },
84
54
  props: {
85
55
  label: {
@@ -118,26 +88,20 @@ export default defineComponent({
118
88
 
119
89
  const colors = computed(() => getColors(props.color));
120
90
  const lights = getColors(ColorEnum.Light);
121
- const darks = getColors(ColorEnum.Dark);
122
91
 
123
92
  const style = computed((): StyleValue => {
124
93
  if (props.disabled) {
125
94
  return {
126
- "--fs-slider-cursor" : "default",
127
- "--fs-slider-track-color": lights.base,
128
- "--fs-slider-thumb-color": lights.base,
129
- "--fs-slider-color" : lights.dark
95
+ "--fs-slider-thumb-color": lights.base
130
96
  };
131
97
  }
132
98
  return {
133
- "--fs-slider-cursor" : "pointer",
134
- "--fs-slider-track-color": colors.value.light,
135
- "--fs-slider-thumb-color": colors.value.base,
136
- "--fs-slider-color" : darks.base
99
+ "--fs-slider-thumb-color": colors.value.base
137
100
  };
138
101
  });
139
102
 
140
103
  return {
104
+ colors,
141
105
  style
142
106
  };
143
107
  }
@@ -17,19 +17,24 @@
17
17
  align="top-left"
18
18
  >
19
19
  <FSRow
20
- v-if="$props.showSearch || filterableHeaders.length > 0 || $slots['append-toolbar']"
20
+ v-if="$props.showSearch || $slots['toolbar']"
21
21
  align="bottom-left"
22
22
  >
23
- <FSSearchField
24
- :hideHeader="true"
25
- v-model="innerSearch"
26
- />
27
- <FSButton
28
- v-if="filterableHeaders.length > 0"
29
- prependIcon="mdi-filter-variant"
30
- :variant="innerShowFilters ? 'full' : 'standard'"
31
- @click="innerShowFilters = !innerShowFilters"
32
- />
23
+ <template
24
+ v-if="$props.showSearch"
25
+ >
26
+ <FSSearchField
27
+ v-if="$props.showSearch"
28
+ :hideHeader="true"
29
+ v-model="innerSearch"
30
+ />
31
+ <FSButton
32
+ v-if="filterableHeaders.length > 0"
33
+ prependIcon="mdi-filter-variant"
34
+ :variant="innerShowFilters ? 'full' : 'standard'"
35
+ @click="innerShowFilters = !innerShowFilters"
36
+ />
37
+ </template>
33
38
  <slot
34
39
  v-if="!isMobileSized"
35
40
  name="toolbar"
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@dative-gpi/foundation-shared-components",
3
3
  "sideEffects": false,
4
- "version": "1.0.161",
4
+ "version": "1.0.162",
5
5
  "description": "",
6
6
  "publishConfig": {
7
7
  "access": "public"
@@ -10,8 +10,8 @@
10
10
  "author": "",
11
11
  "license": "ISC",
12
12
  "dependencies": {
13
- "@dative-gpi/foundation-shared-domain": "1.0.161",
14
- "@dative-gpi/foundation-shared-services": "1.0.161"
13
+ "@dative-gpi/foundation-shared-domain": "1.0.162",
14
+ "@dative-gpi/foundation-shared-services": "1.0.162"
15
15
  },
16
16
  "peerDependencies": {
17
17
  "@dative-gpi/bones-ui": "^1.0.0",
@@ -35,5 +35,5 @@
35
35
  "sass": "1.71.1",
36
36
  "sass-loader": "13.3.2"
37
37
  },
38
- "gitHead": "1e826f1ef83221e21bfb2da0688ff3cf4b1ed94b"
38
+ "gitHead": "3b098f1cccbf68b3917a8f7d2f627e3e29cde9b4"
39
39
  }
@@ -1,50 +1,10 @@
1
1
  .fs-slider {
2
- padding: 0px !important;
3
- width: calc(100% - 16px);
4
-
5
- & .v-slider__container {
6
- cursor: var(--fs-slider-cursor);
7
- margin-left: 8px;
8
- margin-right: 8px;
9
- opacity: 1 !important;
10
- }
11
-
12
- & .v-slider-track__background {
13
- background-color: var(--fs-slider-track-color);
14
- height: 6px !important;
15
- opacity: 1;
16
- }
17
-
18
2
  & .v-slider-track__fill {
19
3
  display: none;
20
4
  }
21
5
 
22
6
  & .v-slider-track__tick {
23
7
  background-color: var(--fs-slider-thumb-color);
24
- border-radius: 50%;
25
8
  }
26
9
 
27
- & .v-slider-thumb {
28
- color: var(--fs-slider-thumb-color);
29
-
30
- &__surface:before {
31
- display: none !important;
32
- }
33
-
34
- &__surface:after {
35
- display: none !important;
36
- }
37
-
38
- &__ripple {
39
- display: none !important;
40
- }
41
- }
42
- }
43
-
44
- .fs-slider-label {
45
- color: var(--fs-slider-color);
46
- }
47
-
48
- .fs-slider-description {
49
- color: var(--fs-slider-color);
50
10
  }
package/utils/index.ts CHANGED
@@ -9,6 +9,7 @@ export * from "./leafletMarkers"
9
9
  export * from "./levenshtein";
10
10
  export * from "./lexical";
11
11
  export * from "./operations";
12
+ export * from "./picker";
12
13
  export * from "./sort";
13
14
  export * from "./statuses";
14
15
  export * from "./time";
@@ -0,0 +1,40 @@
1
+ export enum TimePrecision {
2
+ Hours = 1,
3
+ Days = 2,
4
+ Months = 3
5
+ }
6
+
7
+ /**
8
+ * Compute ticks for a time range based on the specified precision.
9
+ */
10
+ export const computeTicks = ({ start, end, tickCount, precision }: { start: number, end: number, tickCount: number, precision: TimePrecision }): number[] => {
11
+ const ticks: number[] = [];
12
+ const range = end - start;
13
+
14
+ if (precision === TimePrecision.Hours) {
15
+ // Tick toutes les X heures, aligné sur l'heure pleine
16
+ const step = Math.ceil(range / tickCount / 3600000) * 3600000;
17
+ const alignedStart = Math.ceil(start / 3600000) * 3600000;
18
+ for (let i = 0; i < tickCount; i++) {
19
+ const tick = alignedStart + i * step;
20
+ if (tick < end) { ticks.push(tick); }
21
+ }
22
+ } else if (precision === TimePrecision.Days) {
23
+ // Tick tous les X jours, aligné sur minuit
24
+ const step = Math.ceil(range / tickCount / 86400000) * 86400000;
25
+ const date = new Date(start);
26
+ date.setHours(0, 0, 0, 0);
27
+ const alignedStart = date.getTime() + (date.getTime() < start ? step : 0);
28
+ for (let i = 0; i < tickCount; i++) {
29
+ const tick = alignedStart + i * step;
30
+ if (tick < end) { ticks.push(tick); }
31
+ }
32
+ } else {
33
+ // Tick tous les X mois (approximation linéaire)
34
+ const interval = range / tickCount;
35
+ for (let i = 0; i < tickCount; i++) {
36
+ ticks.push(start + i * interval);
37
+ }
38
+ }
39
+ return ticks;
40
+ }