@dative-gpi/foundation-shared-components 1.0.128 → 1.0.130-maps2

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.
Files changed (55) hide show
  1. package/components/FSBreadcrumbs.vue +20 -12
  2. package/components/FSChip.vue +2 -2
  3. package/components/FSDialogMenu.vue +17 -8
  4. package/components/FSDialogRemove.vue +1 -1
  5. package/components/FSFadeOut.vue +24 -9
  6. package/components/FSIconCard.vue +26 -3
  7. package/components/FSInstantPicker.vue +266 -0
  8. package/components/FSPlayButtons.vue +72 -0
  9. package/components/FSProgressBar.vue +94 -0
  10. package/components/FSSpan.vue +8 -5
  11. package/components/FSText.vue +7 -5
  12. package/components/deviceOrganisations/FSStatusRichCard.vue +170 -0
  13. package/components/fields/FSAutocompleteField.vue +36 -52
  14. package/components/fields/FSDateField.vue +1 -0
  15. package/components/fields/FSSelectField.vue +41 -53
  16. package/components/fields/FSTermField.vue +11 -8
  17. package/components/fields/FSTranslateRichTextField.vue +17 -2
  18. package/components/fields/periodicField/FSPeriodicWeeklyField.vue +21 -11
  19. package/components/lists/FSDataTableUI.vue +30 -19
  20. package/components/map/FSMap.vue +19 -10
  21. package/components/map/FSMapMarker.vue +4 -4
  22. package/components/map/FSMapMarkerClusterGroup.vue +1 -1
  23. package/components/map/FSMapOverlay.vue +34 -19
  24. package/components/tiles/FSGroupTileUI.vue +14 -1
  25. package/components/tiles/FSLocationTileUI.vue +1 -1
  26. package/components/tiles/FSSimpleTileUI.vue +1 -1
  27. package/components/views/desktop/FSBaseDefaultDesktopView.vue +8 -7
  28. package/components/views/desktop/FSBaseEntityDesktopView.vue +1 -0
  29. package/components/views/mobile/FSBaseDefaultMobileView.vue +8 -7
  30. package/components/views/mobile/FSBaseEntityMobileView.vue +1 -0
  31. package/composables/useSlots.ts +2 -1
  32. package/models/rules.ts +5 -2
  33. package/package.json +4 -4
  34. package/styles/components/fs_breadcrumbs.scss +19 -31
  35. package/styles/components/fs_button.scss +7 -5
  36. package/styles/components/fs_chip.scss +8 -6
  37. package/styles/components/fs_clickable.scss +14 -12
  38. package/styles/components/fs_data_iterator_item.scss +12 -10
  39. package/styles/components/fs_dialog.scss +1 -1
  40. package/styles/components/fs_dialog_menu.scss +4 -2
  41. package/styles/components/fs_image_card.scss +5 -3
  42. package/styles/components/fs_map.scss +48 -16
  43. package/styles/components/fs_password_field.scss +4 -2
  44. package/styles/components/fs_progress_bar.scss +14 -0
  45. package/styles/components/fs_select_field.scss +4 -0
  46. package/styles/components/fs_span.scss +12 -4
  47. package/styles/components/fs_status_rich_card.scss +13 -0
  48. package/styles/components/fs_tabs.scss +9 -5
  49. package/styles/components/fs_tag.scss +9 -7
  50. package/styles/components/index.scss +2 -0
  51. package/styles/globals/overrides.scss +11 -4
  52. package/styles/globals/scrollbars.scss +10 -0
  53. package/utils/index.ts +1 -0
  54. package/utils/operations.ts +69 -0
  55. package/components/tiles/FSChartTile.vue +0 -73
package/models/rules.ts CHANGED
@@ -2,8 +2,9 @@ import { useTranslations as useTranslationsProvider } from "@dative-gpi/bones-ui
2
2
  import { useDateFormat } from "@dative-gpi/foundation-shared-services/composables";
3
3
  import { validateExpression } from "@dative-gpi/foundation-shared-domain/tools";
4
4
 
5
- import { getTimeBestString } from "../utils";
6
- import type { TimeUnit } from "@/shared/foundation-shared-domain/enums";
5
+ import type { TimeUnit } from "@dative-gpi/foundation-shared-domain/enums";
6
+
7
+ import { getTimeBestString, validateOperation } from "../utils";
7
8
 
8
9
  const { epochToLongDateFormat } = useDateFormat()!;
9
10
  const { $tr } = useTranslationsProvider();
@@ -22,6 +23,8 @@ export const TextRules = {
22
23
  different: (original: string, message: string | undefined = undefined) => (value: string) => value != original || (message ?? $tr("ui.rules.text-different", "Must be different from original")),
23
24
  outside: (values: string[], message: string | undefined = undefined) => (value: string) => !values.includes(value) || (message ?? $tr("ui.rules.text-outside", "Must be different from others")),
24
25
  inside: (values: string[], message: string | undefined = undefined) => (value: string) => values.includes(value) || (message ?? $tr("ui.rules.text-inside", "Must be one of the options")),
26
+ operation: (operands: string[], variables: string[], message: string | undefined = undefined) => (value: string) => validateOperation(value, operands, variables) || (message ?? $tr("ui.rules.text-operation", "Invalid operation")),
27
+ singleWord: (message: string | undefined = undefined) => (value: string) => !value.includes(" ") || (message ?? $tr("ui.rules.text-single-word", "Must be a single word")),
25
28
  };
26
29
 
27
30
  export const TagRules = {
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.128",
4
+ "version": "1.0.130-maps2",
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.128",
14
- "@dative-gpi/foundation-shared-services": "1.0.128"
13
+ "@dative-gpi/foundation-shared-domain": "1.0.130-maps2",
14
+ "@dative-gpi/foundation-shared-services": "1.0.130-maps2"
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": "ca2de0b5abd0fb4f1fdfaafbc4267182ab609336"
38
+ "gitHead": "a0739b48282166f721cf6c2376a430e4690d92ef"
39
39
  }
@@ -1,48 +1,36 @@
1
- .fs-breadcrumbs-label {
2
- cursor: pointer;
3
- user-select: none;
4
- color: var(--fs-breadcrumbs-color);
5
-
6
- &-disabled {
7
- color: var(--fs-breadcrumbs-disabled-color) !important;
8
- }
9
-
10
- &:hover {
11
- text-decoration: underline;
12
- }
1
+ .fs-breadcrumbs {
2
+ height: var(--fs-breadcrumbs-height) !important;
3
+ padding: 0 !important;
4
+ gap: 0 !important;
13
5
 
14
- &:active {
15
- color: var(--fs-breadcrumbs-active-color) !important;
16
- }
6
+ max-width: 100%;
17
7
  }
18
8
 
19
- // .fs-breadcrumbs-divider {
20
- // margin-bottom: 2px !important;
21
- // }
22
-
23
- .fs-breadcrumbs.v-breadcrumbs {
24
- line-height: normal!important;
9
+ .v-breadcrumbs-item,
10
+ .v-breadcrumbs-item--link {
25
11
  padding: 0 !important;
26
- gap: 8px !important;
27
- height: 20px;
28
- }
12
+ user-select: none;
13
+ cursor: pointer;
29
14
 
30
- .v-breadcrumbs-item {
31
- padding: 0 !important;
15
+ align-self: stretch;
16
+ display: flex;
17
+ flex: 1 0 0;
18
+
19
+ max-width: fit-content;
20
+ min-width: 20px;
32
21
 
33
22
  &--disabled {
34
23
  opacity: 1 !important;
35
24
  color: var(--fs-breadcrumbs-disabled-color) !important;
36
25
  }
26
+ }
37
27
 
38
- &:last-child > .fs-breadcrumbs-label-disabled {
39
- @extend .text-button;
28
+ .v-breadcrumbs-item:last-child > .fs-breadcrumbs-label {
29
+ @extend .text-button;
40
30
 
41
- color: var(--fs-breadcrumbs-active-color) !important;
42
- }
31
+ color: var(--fs-breadcrumbs-active-color) !important;
43
32
  }
44
33
 
45
34
  .v-breadcrumbs-divider {
46
- padding: 0 !important;
47
35
  color: var(--fs-breadcrumbs-color) !important;
48
36
  }
@@ -2,11 +2,6 @@
2
2
  transition: color 0.28s cubic-bezier(0.4, 0, 0.2, 1) !important;
3
3
  color: var(--fs-button-color) !important;
4
4
 
5
- &:not(.fs-button-disabled, :has(.fs-button-load)):hover {
6
- color: var(--fs-button-hover-color) !important;
7
- cursor: pointer !important;
8
- }
9
-
10
5
  &.fs-button-disabled,
11
6
  &:has(.fs-button-load) {
12
7
  cursor: default !important;
@@ -16,4 +11,11 @@
16
11
  color: inherit;
17
12
  text-decoration: none;
18
13
  }
14
+
15
+ @include clickscreen {
16
+ &:not(.fs-button-disabled, :has(.fs-button-load)):hover {
17
+ color: var(--fs-button-hover-color) !important;
18
+ cursor: pointer !important;
19
+ }
20
+ }
19
21
  }
@@ -12,15 +12,17 @@
12
12
  .fs-chip-editable {
13
13
  cursor: pointer !important;
14
14
 
15
- &:hover {
16
- background-color: var(--fs-chip-hover-background-color) !important;
17
- border-color: var(--fs-chip-hover-border-color) !important;
18
- color: var(--fs-chip-hover-color) !important;
19
- }
20
-
21
15
  &:active {
22
16
  background-color: var(--fs-chip-active-background-color) !important;
23
17
  border-color: var(--fs-chip-active-border-color) !important;
24
18
  color: var(--fs-chip-active-color) !important;
25
19
  }
20
+
21
+ @include clickscreen {
22
+ &:hover {
23
+ background-color: var(--fs-chip-hover-background-color) !important;
24
+ border-color: var(--fs-chip-hover-border-color) !important;
25
+ color: var(--fs-chip-hover-color) !important;
26
+ }
27
+ }
26
28
  }
@@ -11,18 +11,7 @@
11
11
  &.fs-clickable-disabled {
12
12
  cursor: default;
13
13
  }
14
-
15
- &:not(.fs-clickable-disabled):hover:not(:has( .fs-stopclick:hover)) {
16
- background-color: var(--fs-clickable-hover-background-color) !important;
17
- border-color: var(--fs-clickable-hover-border-color) !important;
18
- color: var(--fs-clickable-hover-color) !important;
19
-
20
- & .fs-card-clickable {
21
- transition: background-color 0.28s cubic-bezier(0.4, 0, 0.2, 1);
22
- background-color: var(--fs-clickable-hover-background-color);
23
- }
24
- }
25
-
14
+
26
15
  &:not(.fs-clickable-disabled):active:not(:has( .fs-stopclick:hover)) {
27
16
  background-color: var(--fs-clickable-active-background-color) !important;
28
17
  border-color: var(--fs-clickable-active-border-color) !important;
@@ -33,6 +22,19 @@
33
22
  background-color: var(--fs-clickable-active-background-color);
34
23
  }
35
24
  }
25
+
26
+ @include clickscreen {
27
+ &:not(.fs-clickable-disabled):hover:not(:has( .fs-stopclick:hover)) {
28
+ background-color: var(--fs-clickable-hover-background-color) !important;
29
+ border-color: var(--fs-clickable-hover-border-color) !important;
30
+ color: var(--fs-clickable-hover-color) !important;
31
+
32
+ & .fs-card-clickable {
33
+ transition: background-color 0.28s cubic-bezier(0.4, 0, 0.2, 1);
34
+ background-color: var(--fs-clickable-hover-background-color);
35
+ }
36
+ }
37
+ }
36
38
  }
37
39
 
38
40
  a:has(.fs-clickable) {
@@ -8,16 +8,18 @@
8
8
  padding-right: 32px;
9
9
  }
10
10
 
11
- &:hover::after {
12
- content: "";
13
- background-color: black;
14
- pointer-events: none;
15
- position: absolute;
16
- opacity: 0.04;
17
- height: 100%;
18
- width: 100%;
19
- left: 0;
20
- top: 0;
11
+ @include clickscreen {
12
+ &:hover::after {
13
+ content: "";
14
+ background-color: black;
15
+ pointer-events: none;
16
+ position: absolute;
17
+ opacity: 0.04;
18
+ height: 100%;
19
+ width: 100%;
20
+ left: 0;
21
+ top: 0;
22
+ }
21
23
  }
22
24
  }
23
25
 
@@ -1,5 +1,5 @@
1
1
  .fs-dialog-mobile > .v-overlay__content {
2
- max-height: 100vh !important;
2
+ max-height: 100dvh !important;
3
3
  max-width: 100vw !important;
4
4
  width: 100% !important;
5
5
  margin: 0 !important;
@@ -1,11 +1,13 @@
1
1
  .v-overlay__content:has(.fs-dialog-menu) {
2
2
  width: fit-content !important;
3
- max-height: calc(100vh - 40px) !important;
3
+ max-height: calc(100dvh - 40px) !important;
4
+ max-width: calc(100vw - 40px) !important;
4
5
  justify-content: center;
5
6
  align-items: center;
7
+ margin: 20px;
6
8
  }
7
9
 
8
10
  .fs-dialog-menu {
9
- max-height: calc(100vh - 40px) !important;
11
+ max-height: calc(100dvh - 40px) !important;
10
12
  max-width: calc(100vw - 40px) !important;
11
13
  }
@@ -11,8 +11,10 @@
11
11
  }
12
12
  }
13
13
 
14
- .fs-image-card:hover {
15
- > .fs-card {
16
- background-size: 105%;
14
+ @include clickscreen {
15
+ .fs-image-card:hover {
16
+ > .fs-card {
17
+ background-size: 105%;
18
+ }
17
19
  }
18
20
  }
@@ -77,31 +77,26 @@
77
77
  }
78
78
  }
79
79
 
80
- .fs-map-point-pin {
81
- background-color: var(--fs-map-point-pin-color);
82
- border-radius: 100%;
83
- }
84
-
85
- .fs-map-location > div {
80
+ .fs-map-marker > div {
86
81
  display: flex;
87
82
  height: 100%;
88
- color: var(--fs-map-location-pin-color);
89
83
  border-radius: 50%;
90
- background-color: white;
91
84
  filter: drop-shadow(0px 2px 4px rgba(0, 0, 0, 0.4));
92
85
  align-items: center;
93
86
  justify-content: center;
94
87
 
95
- &:hover {
96
- filter: brightness(0.92) drop-shadow(0px 2px 4px rgba(0, 0, 0, 0.4));
97
-
98
- > * {
99
- transform: scale(1.15);
88
+ @include clickscreen {
89
+ &:hover {
90
+ filter: brightness(0.92) drop-shadow(0px 2px 4px rgba(0, 0, 0, 0.4));
91
+
92
+ > * {
93
+ transform: scale(1.15);
94
+ }
100
95
  }
101
96
  }
102
97
  }
103
98
 
104
- .fs-map-location-full > div {
99
+ .fs-map-cluster-marker > div {
105
100
  background-color: var(--fs-map-location-pin-color);
106
101
  color: white;
107
102
  }
@@ -124,12 +119,49 @@
124
119
  }
125
120
  }
126
121
 
122
+ .fs-map-location > div {
123
+ color: var(--fs-map-location-pin-color);
124
+ background-color: white;
125
+ }
126
+
127
+ .fs-map-pin > div {
128
+ background-color: var(--fs-map-point-pin-color);
129
+ position: relative;
130
+
131
+ transition: transform 0.28s cubic-bezier(0.4, 0, 0.2, 1);
132
+ }
133
+
134
+ .fs-map-pin > div::before {
135
+ content: "";
136
+ position: absolute;
137
+ top: -4px;
138
+ left: -4px;
139
+ width: calc(100% + 8px);
140
+ height: calc(100% + 8px);
141
+ border-radius: 50%;
142
+ border: 2px solid var(--fs-map-point-pin-color);
143
+ opacity: 0.4;
144
+
145
+ @include clickscreen {
146
+ &:hover {
147
+ opacity: 1;
148
+ }
149
+ }
150
+ }
151
+
152
+ .fs-map-pin-selected > div {
153
+ transform: scale(1.35);
154
+ }
155
+
156
+
127
157
  .fs-map-site {
128
158
  opacity: 0.6;
129
159
  transition: opacity 0.28s cubic-bezier(0.4, 0, 0.2, 1);
130
160
 
131
- &:hover {
132
- opacity: 1;
161
+ @include clickscreen {
162
+ &:hover {
163
+ opacity: 1;
164
+ }
133
165
  }
134
166
  }
135
167
  }
@@ -4,7 +4,9 @@
4
4
  color: var(--fs-password-field-color) !important;
5
5
  transition: color 0.28s cubic-bezier(0.4, 0, 0.2, 1);
6
6
 
7
- &:hover {
8
- color: var(--fs-password-field-hover-color) !important;
7
+ @include clickscreen {
8
+ &:hover {
9
+ color: var(--fs-password-field-hover-color) !important;
10
+ }
9
11
  }
10
12
  }
@@ -0,0 +1,14 @@
1
+ .fs-progress-bar-gradient {
2
+ flex: 1;
3
+ background-color: var(--progress-bar-background-color);
4
+ height: 8px;
5
+ border-radius: 4px;
6
+
7
+ div {
8
+ transition: all 0.28s cubic-bezier(0.4, 0, 0.2, 1);
9
+ 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);
12
+ border-radius: 4px;
13
+ }
14
+ }
@@ -32,4 +32,8 @@
32
32
  border-radius: 4px !important;
33
33
  margin-top: 2px !important;
34
34
  }
35
+ }
36
+
37
+ .fs-select-field-multiple-slide-group {
38
+ opacity: var(--fs-select-field-multiple-opacity);
35
39
  }
@@ -1,9 +1,17 @@
1
1
  .fs-span {
2
+ display: flex;
3
+
4
+ max-width: 100%;
5
+ min-width: 0;
6
+ }
7
+
8
+ .fs-span > span {
9
+ text-align: var(--fs-span-text-align);
10
+ align-content: center;
2
11
  max-width: 100%;
3
- text-align: left;
4
12
  }
5
13
 
6
- .fs-span-line-clamp {
14
+ .fs-span-line-clamp > span {
7
15
  overflow: hidden;
8
16
  display: -webkit-box;
9
17
  line-clamp: var(--fs-span-line-clamp);
@@ -11,12 +19,12 @@
11
19
  -webkit-line-clamp: var(--fs-span-line-clamp);
12
20
  }
13
21
 
14
- .fs-span-ellipsis {
22
+ .fs-span-ellipsis > span {
15
23
  overflow: hidden;
16
24
  white-space: nowrap;
17
25
  text-overflow: ellipsis;
18
26
  }
19
27
 
20
- .fs-span-pre-wrap {
28
+ .fs-span-pre-wrap > span {
21
29
  white-space: pre-wrap;
22
30
  }
@@ -0,0 +1,13 @@
1
+ .fs-status-rich-card {
2
+ position: relative;
3
+ flex-grow: 1;
4
+ background-color: var(--fs-status-rich-card-background-color);
5
+ border-color: var(--fs-status-rich-card-border-color);
6
+ }
7
+
8
+ .fs-status-rich-card-corner {
9
+ position: absolute;
10
+ display: flex;
11
+ right: 2px;
12
+ top: 2px;
13
+ }
@@ -23,9 +23,11 @@
23
23
  @extend .text-button;
24
24
  }
25
25
 
26
- &:hover {
27
- border-bottom: 1px solid var(--fs-tab-hover-border-color) !important;
28
- color: var(--fs-group-hover-color) !important;
26
+ @include clickscreen {
27
+ &:hover {
28
+ border-bottom: 1px solid var(--fs-tab-hover-border-color) !important;
29
+ color: var(--fs-group-hover-color) !important;
30
+ }
29
31
  }
30
32
  }
31
33
 
@@ -34,8 +36,10 @@
34
36
  color: var(--fs-group-hover-color) !important;
35
37
  border-bottom: 0 !important;
36
38
 
37
- &:hover {
38
- border-bottom: 0 !important;
39
+ @include clickscreen {
40
+ &:hover {
41
+ border-bottom: 0 !important;
42
+ }
39
43
  }
40
44
  }
41
45
 
@@ -13,17 +13,19 @@
13
13
  background-color: var(--fs-tag-background-color) !important;
14
14
  color: var(--fs-tag-color) !important;
15
15
 
16
- &:hover {
17
- background-color: var(--fs-tag-hover-background-color) !important;
18
- color: var(--fs-tag-hover-color) !important;
19
- }
16
+ min-width: 20px !important;
17
+ width: 20px !important;
18
+ height: 20px !important;
20
19
 
21
20
  &:active {
22
21
  background-color: var(--fs-tag-active-background-color) !important;
23
22
  color: var(--fs-tag-active-color) !important;
24
23
  }
25
24
 
26
- min-width: 20px !important;
27
- width: 20px !important;
28
- height: 20px !important;
25
+ @include clickscreen {
26
+ &:hover {
27
+ background-color: var(--fs-tag-hover-background-color) !important;
28
+ color: var(--fs-tag-hover-color) !important;
29
+ }
30
+ }
29
31
  }
@@ -47,6 +47,7 @@
47
47
  @import "fs_option_group.scss";
48
48
  @import "fs_pagination.scss";
49
49
  @import "fs_password_field.scss";
50
+ @import "fs_progress_bar.scss";
50
51
  @import "fs_radio.scss";
51
52
  @import "fs_rich_text_field.scss";
52
53
  @import "fs_row.scss";
@@ -54,6 +55,7 @@
54
55
  @import "fs_slide_group.scss";
55
56
  @import "fs_slider.scss";
56
57
  @import "fs_span.scss";
58
+ @import "fs_status_rich_card.scss";
57
59
  @import "fs_switch.scss";
58
60
  @import "fs_tag.scss";
59
61
  @import "fs_tabs.scss";
@@ -137,11 +137,16 @@
137
137
  }
138
138
  }
139
139
 
140
- // Ellipsis on input of all fields
141
140
  input {
141
+ // Ellipsis on all fields
142
142
  text-overflow: ellipsis;
143
143
  }
144
144
 
145
+ input, select, textarea {
146
+ // No zoom on focus for user of a certain version of Chrome, and who ask their mobile to zoom if font-size < 16px
147
+ touch-action: none;
148
+ }
149
+
145
150
  // No up / down buttons in input field of type number
146
151
  input[type=number] {
147
152
  -moz-appearance: textfield;
@@ -187,9 +192,11 @@ $nthOverlay: 25;
187
192
  }
188
193
 
189
194
  // Change color on arrows when hovered
190
- .v-slide-group__prev:hover,
191
- .v-slide-group__next:hover {
192
- color: var(--fs-group-hover-color);
195
+ @include clickscreen {
196
+ .v-slide-group__prev:hover,
197
+ .v-slide-group__next:hover {
198
+ color: var(--fs-group-hover-color);
199
+ }
193
200
  }
194
201
 
195
202
  /***************************************************************************/
@@ -47,9 +47,19 @@
47
47
  @include touchscreen {
48
48
  .fs-hide-x-scrollbar {
49
49
  overflow-x: scroll;
50
+
51
+ &::-webkit-scrollbar {
52
+ display: none;
53
+ }
54
+ scrollbar-width: none;
50
55
  }
51
56
 
52
57
  .fs-hide-y-scrollbar {
53
58
  overflow-y: scroll;
59
+
60
+ &::-webkit-scrollbar {
61
+ display: none;
62
+ }
63
+ scrollbar-width: none;
54
64
  }
55
65
  }
package/utils/index.ts CHANGED
@@ -8,6 +8,7 @@ export * from "./icons";
8
8
  export * from "./leafletMarkers"
9
9
  export * from "./levenshtein";
10
10
  export * from "./lexical";
11
+ export * from "./operations";
11
12
  export * from "./sort";
12
13
  export * from "./statuses";
13
14
  export * from "./time";
@@ -0,0 +1,69 @@
1
+ const MinusOperator = "-";
2
+
3
+ // Matches one of the three operators (+, *, /) or the - operator if it is preceded by something that is not another operator
4
+ const operatorsRegex = new RegExp(/[\+\*\/]|(?<=.)(?<![-\+\*\/])[-]/gm);
5
+
6
+ // Matches a nested block of parenthesis
7
+ const parenthesisRegex = new RegExp(/\([^)(]+\)/gm);
8
+
9
+ // Match a positive decimal number
10
+ const decimalRegex = new RegExp(/^\d+(.\d+)?$/gm);
11
+
12
+ const validateBlock = (block: string, operands: string[] = [], variables: string[] = []) => {
13
+ // Remove parenthesis from the block
14
+ block = block.replaceAll("(", "").replaceAll(")", "");
15
+
16
+ // Split block on operators (Leave negative signs)
17
+ const components = block.split(operatorsRegex);
18
+
19
+ // Check if each bit is a valid operand
20
+ for (let i = 0; i < components.length; i++) {
21
+ // Remove negative sign
22
+ if (components[i].startsWith(MinusOperator)) {
23
+ components[i] = components[i].substring(1);
24
+ }
25
+ if (!operands.includes(components[i]) && !variables.includes(components[i]) && !components[i].match(decimalRegex)) {
26
+ return false;
27
+ }
28
+ }
29
+ return true;
30
+ }
31
+
32
+ export const validateOperation = (rawOperation: string, operands: string[] = [], variables: string[] = []) => {
33
+ // Remove spaces
34
+ let operation = rawOperation.replaceAll(" ", "");
35
+
36
+ // Check parenthesis
37
+ const parenthesis: number[] = [];
38
+
39
+ for (let i = 0; i < operation.length; i++) {
40
+ if (operation[i] == '(') {
41
+ parenthesis.push(i);
42
+ }
43
+ else if (operation[i] == ')') {
44
+ if (parenthesis.length > 0) {
45
+ if (i == parenthesis.pop()! + 1) {
46
+ return false;
47
+ }
48
+ }
49
+ else {
50
+ return false;
51
+ }
52
+ }
53
+ }
54
+ if (parenthesis.length > 0) {
55
+ return false;
56
+ }
57
+
58
+ // Check each block between parenthesis
59
+ let match = operation.match(parenthesisRegex);
60
+
61
+ while (match?.[0]) {
62
+ if (!validateBlock(match[0], operands, variables)) {
63
+ return false;
64
+ }
65
+ operation = operation.replace(match[0], "1");
66
+ match = operation.match(parenthesisRegex);
67
+ }
68
+ return validateBlock(operation, operands, variables);
69
+ }