@dative-gpi/foundation-shared-components 1.1.20 → 1.1.21-fs-chart-2

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.
@@ -4,21 +4,62 @@
4
4
  :style="style"
5
5
  >
6
6
  <div
7
- class="fs-progress-bar-gradient"
7
+ class="fs-progress-bar-wrapper"
8
8
  >
9
- <div></div>
9
+ <div
10
+ class="fs-progress-bar-track"
11
+ >
12
+ <div
13
+ v-if="$props.cursor && isValueInRange"
14
+ class="fs-progress-bar-cursor"
15
+ >
16
+ <FSText
17
+ v-if="$props.showValue"
18
+ class="fs-progress-bar-cursor-label"
19
+ font="text-overline"
20
+ :ellipsis="false"
21
+ >
22
+ {{ displayValue }}
23
+ </FSText>
24
+ </div>
25
+ <div
26
+ v-if="!$props.cursor"
27
+ class="fs-progress-bar-fill"
28
+ ></div>
29
+ </div>
30
+ <div
31
+ v-if="positionedLabels.length"
32
+ class="fs-progress-bar-labels"
33
+ >
34
+ <div
35
+ v-for="label in positionedLabels"
36
+ :key="label.value"
37
+ class="fs-progress-bar-label"
38
+ :class="{
39
+ 'fs-progress-bar-label--start': label.percent === 0,
40
+ 'fs-progress-bar-label--end': label.percent === 100
41
+ }"
42
+ :style="{ left: `${label.percent}%` }"
43
+ >
44
+ <FSText
45
+ font="text-overline"
46
+ >
47
+ {{ label.display }}
48
+ </FSText>
49
+ </div>
50
+ </div>
10
51
  </div>
11
52
  <FSText
12
- v-if="$props.showValue"
53
+ v-if="$props.showValue && !$props.cursor"
13
54
  font="text-button"
14
55
  >
15
- {{ fixedRate }}%
56
+ {{ displayValue }}
16
57
  </FSText>
17
58
  </FSRow>
18
59
  </template>
19
60
 
20
61
  <script lang="ts">
21
- import { computed, defineComponent, type StyleValue } from "vue";
62
+ import { computed, defineComponent, type PropType, type StyleValue } from "vue";
22
63
 
23
64
  import { useColors } from '@dative-gpi/foundation-shared-components/composables';
24
65
 
@@ -50,6 +91,31 @@ export default defineComponent({
50
91
  type: Boolean,
51
92
  required: false,
52
93
  default: true
94
+ },
95
+ valueFormat: {
96
+ type: String as PropType<"percentage" | "raw">,
97
+ required: false,
98
+ default: "percentage"
99
+ },
100
+ min: {
101
+ type: Number,
102
+ required: false,
103
+ default: 0
104
+ },
105
+ max: {
106
+ type: Number,
107
+ required: false,
108
+ default: 1
109
+ },
110
+ labels: {
111
+ type: Array as PropType<Array<{ value: number; text?: string }>>,
112
+ required: false,
113
+ default: () => []
114
+ },
115
+ cursor: {
116
+ type: Boolean,
117
+ required: false,
118
+ default: false
53
119
  }
54
120
  },
55
121
  setup(props) {
@@ -58,37 +124,85 @@ export default defineComponent({
58
124
  const lightColors = getColors(ColorEnum.Light);
59
125
  const successColors = getColors(ColorEnum.Success);
60
126
  const errorColors = getColors(ColorEnum.Error);
61
-
62
- const fixedRate = computed(() => {
63
- return (props.modelValue * 100).toFixed(0);
127
+
128
+ const isValid = computed(() => props.max > props.min);
129
+
130
+ const range = computed(() => props.max - props.min);
131
+
132
+ const clampedValue = computed(() => {
133
+ if (!isValid.value) {
134
+ return props.min;
135
+ }
136
+ return Math.min(Math.max(props.modelValue, props.min), props.max);
137
+ });
138
+
139
+ const valuePercent = computed(() => {
140
+ if (!isValid.value) {
141
+ return 0;
142
+ }
143
+ return ((clampedValue.value - props.min) / range.value) * 100;
144
+ });
145
+
146
+ const isValueInRange = computed(() => {
147
+ return props.modelValue >= props.min && props.modelValue <= props.max;
148
+ });
149
+
150
+ const zeroPercent = computed(() => {
151
+ if (!isValid.value) { return 0; }
152
+ const zero = Math.min(Math.max(0, props.min), props.max);
153
+ return ((zero - props.min) / range.value) * 100;
64
154
  });
65
155
 
66
- const relativeWidth = computed(() => {
67
- return props.modelValue ? 100 / props.modelValue : 0;
156
+ const fillLeft = computed(() => Math.min(zeroPercent.value, valuePercent.value));
157
+
158
+ const fillWidth = computed(() => Math.abs(valuePercent.value - zeroPercent.value));
159
+
160
+ const gradientStartStop = computed(() => {
161
+ if (fillWidth.value === 0) { return "0%"; }
162
+ return `${-(fillLeft.value / fillWidth.value) * 100}%`;
68
163
  });
69
-
70
- const startColor = computed(() => {
71
- return props.startColor ?? errorColors.base;
164
+
165
+ const gradientEndStop = computed(() => {
166
+ if (fillWidth.value === 0) { return "100%"; }
167
+ return `${((100 - fillLeft.value) / fillWidth.value) * 100}%`;
72
168
  });
73
169
 
74
- const endColor = computed(() => {
75
- return props.endColor ?? successColors.base;
170
+ const positionedLabels = computed(() => {
171
+ return props.labels.map(label => {
172
+ const percent = isValid.value
173
+ ? ((label.value - props.min) / range.value) * 100
174
+ : 0;
175
+
176
+ return {
177
+ value: label.value,
178
+ display: label.text ?? label.value,
179
+ percent: Math.min(Math.max(percent, 0), 100)
180
+ };
181
+ });
76
182
  });
77
183
 
78
- const style = computed((): StyleValue => {
79
- return {
80
- '--progress-bar-background-color': lightColors.dark,
81
- '--progress-bar-gradient-start-color': startColor.value,
82
- '--progress-bar-gradient-end-color': endColor.value,
83
- '--progress-bar-gradient-width': `min(100%, ${fixedRate.value}%)`,
84
- '--progress-bar-total-relative-width': `${relativeWidth.value}%`
85
- };
184
+ const displayValue = computed(() => {
185
+ if (props.valueFormat === "raw") { return props.modelValue.toFixed(2); }
186
+ return `${Math.round(valuePercent.value)}%`;
86
187
  });
87
188
 
189
+ const style = computed((): StyleValue => ({
190
+ "--progress-bar-background": lightColors.dark,
191
+ "--progress-bar-gradient-start": props.startColor ?? errorColors.base,
192
+ "--progress-bar-gradient-start-stop": gradientStartStop.value,
193
+ "--progress-bar-gradient-end": props.endColor ?? successColors.base,
194
+ "--progress-bar-gradient-end-stop": gradientEndStop.value,
195
+ "--progress-bar-fill-left": `${fillLeft.value}%`,
196
+ "--progress-bar-fill-width": `${fillWidth.value}%`,
197
+ "--progress-bar-cursor-position": `${valuePercent.value}%`
198
+ }));
199
+
88
200
  return {
89
- style,
90
- fixedRate
91
- }
92
- },
201
+ positionedLabels,
202
+ isValueInRange,
203
+ displayValue,
204
+ style
205
+ };
206
+ }
93
207
  });
94
- </script>
208
+ </script>
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "url": "https://github.com/Dative-GPI/foundation-shared-ui.git"
5
5
  },
6
6
  "sideEffects": false,
7
- "version": "1.1.20",
7
+ "version": "1.1.21-fs-chart-2",
8
8
  "description": "",
9
9
  "publishConfig": {
10
10
  "access": "public"
@@ -13,11 +13,11 @@
13
13
  "author": "",
14
14
  "license": "ISC",
15
15
  "dependencies": {
16
- "@dative-gpi/foundation-shared-domain": "1.1.20",
17
- "@dative-gpi/foundation-shared-services": "1.1.20"
16
+ "@dative-gpi/foundation-shared-domain": "1.1.21-fs-chart-2",
17
+ "@dative-gpi/foundation-shared-services": "1.1.21-fs-chart-2"
18
18
  },
19
19
  "peerDependencies": {
20
- "@dative-gpi/bones-ui": "^1.0.0",
20
+ "@dative-gpi/bones-ui": "^1.1.0",
21
21
  "@fontsource/montserrat": "^5.0.16",
22
22
  "@lexical/clipboard": "0.12.5",
23
23
  "@lexical/history": "0.12.5",
@@ -38,5 +38,5 @@
38
38
  "sass": "1.71.1",
39
39
  "sass-loader": "13.3.2"
40
40
  },
41
- "gitHead": "539eadec4b1b99e06638b021a59ae11c20cff383"
41
+ "gitHead": "2433c646295815be1606ba105a295cd769d59693"
42
42
  }
@@ -1,14 +1,70 @@
1
- .fs-progress-bar-gradient {
1
+ .fs-progress-bar-wrapper {
2
2
  flex: 1;
3
- background-color: var(--progress-bar-background-color);
3
+ display: flex;
4
+ flex-direction: column;
5
+ gap: 2px;
6
+ }
7
+
8
+ .fs-progress-bar-track {
9
+ position: relative;
10
+ background-color: var(--progress-bar-background);
4
11
  height: 8px;
5
12
  border-radius: 4px;
6
13
 
7
- div {
8
- transition: all 0.28s cubic-bezier(0.4, 0, 0.2, 1);
14
+ .fs-progress-bar-fill {
15
+ position: absolute;
9
16
  height: 100%;
10
- background: linear-gradient(to right, var(--progress-bar-gradient-start-color) 0%, var(--progress-bar-gradient-end-color) var(--progress-bar-total-relative-width));
11
- width: var(--progress-bar-gradient-width);
17
+ left: var(--progress-bar-fill-left);
18
+ width: var(--progress-bar-fill-width);
19
+ background: linear-gradient(
20
+ to right,
21
+ var(--progress-bar-gradient-start) var(--progress-bar-gradient-start-stop),
22
+ var(--progress-bar-gradient-end) var(--progress-bar-gradient-end-stop)
23
+ );
12
24
  border-radius: 4px;
25
+ transition: all 0.28s cubic-bezier(0.4, 0, 0.2, 1);
26
+ }
27
+
28
+ .fs-progress-bar-cursor {
29
+ position: absolute;
30
+ top: 50%;
31
+ left: var(--progress-bar-cursor-position);
32
+ width: 14px;
33
+ height: 14px;
34
+ border-radius: 50%;
35
+ background: linear-gradient(
36
+ to right,
37
+ var(--progress-bar-gradient-start),
38
+ var(--progress-bar-gradient-end)
39
+ );
40
+ transform: translate(-50%, -50%);
41
+ transition: left 0.28s cubic-bezier(0.4, 0, 0.2, 1);
42
+
43
+ .fs-progress-bar-cursor-label {
44
+ position: absolute;
45
+ top: calc(100% + 4px);
46
+ left: 50%;
47
+ transform: translateX(-50%);
48
+ white-space: nowrap;
49
+ pointer-events: none;
50
+ }
51
+ }
52
+ }
53
+
54
+ .fs-progress-bar-labels {
55
+ position: relative;
56
+ height: 16px;
57
+
58
+ .fs-progress-bar-label {
59
+ position: absolute;
60
+ transform: translateX(-50%);
61
+
62
+ &.fs-progress-bar-label--start {
63
+ transform: translateX(0);
64
+ }
65
+
66
+ &.fs-progress-bar-label--end {
67
+ transform: translateX(-100%);
68
+ }
13
69
  }
14
70
  }