@dative-gpi/foundation-shared-components 0.0.87 → 0.0.89

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/FSButton.vue +23 -7
  2. package/components/FSCalendar.vue +3 -1
  3. package/components/FSCalendarTwin.vue +16 -6
  4. package/components/FSCard.vue +9 -3
  5. package/components/FSCheckbox.vue +6 -2
  6. package/components/FSClickable.vue +33 -9
  7. package/components/FSDialog.vue +9 -5
  8. package/components/FSDialogMenu.vue +80 -0
  9. package/components/FSDialogSubmit.vue +0 -1
  10. package/components/FSEditImage.vue +196 -34
  11. package/components/FSImage.vue +21 -7
  12. package/components/FSLink.vue +4 -2
  13. package/components/FSOptionGroup.vue +61 -72
  14. package/components/FSOptionItem.vue +22 -7
  15. package/components/FSRadioGroup.vue +11 -3
  16. package/components/FSToggleSet.vue +22 -60
  17. package/components/FSWindow.vue +2 -0
  18. package/components/autocompletes/FSAutocompleteLanguage.vue +28 -22
  19. package/components/autocompletes/FSAutocompleteTimeZone.vue +117 -17
  20. package/components/buttons/FSButtonFileMini.vue +6 -1
  21. package/components/fields/FSAutocompleteField.vue +139 -72
  22. package/components/fields/FSBaseField.vue +134 -0
  23. package/components/fields/FSColorField.vue +1 -1
  24. package/components/fields/FSDateField.vue +124 -35
  25. package/components/fields/FSDateTimeField.vue +171 -63
  26. package/components/fields/FSIconField.vue +4 -2
  27. package/components/fields/FSNumberField.vue +9 -3
  28. package/components/fields/FSPasswordField.vue +15 -5
  29. package/components/fields/FSRichTextField.vue +34 -18
  30. package/components/fields/FSSearchField.vue +24 -8
  31. package/components/fields/FSSelectField.vue +254 -93
  32. package/components/fields/FSTagField.vue +15 -9
  33. package/components/fields/FSTextArea.vue +31 -59
  34. package/components/fields/FSTextField.vue +22 -70
  35. package/components/fields/FSTimeField.vue +20 -55
  36. package/components/fields/FSTimeSlotField.vue +13 -59
  37. package/components/lists/FSDataIteratorItem.vue +16 -4
  38. package/components/lists/FSDataTableUI.vue +433 -181
  39. package/components/lists/FSDraggable.vue +26 -13
  40. package/components/lists/FSFilterButton.vue +10 -4
  41. package/components/lists/FSHeaderButton.vue +3 -1
  42. package/components/lists/FSHiddenButton.vue +3 -1
  43. package/composables/useAutocomplete.ts +6 -7
  44. package/composables/useSlots.ts +6 -18
  45. package/package.json +4 -4
  46. package/styles/components/fs_base_field.scss +12 -0
  47. package/styles/components/fs_dialog.scss +10 -2
  48. package/styles/components/fs_dialog_menu.scss +11 -0
  49. package/styles/components/fs_draggable.scss +12 -0
  50. package/styles/components/fs_edit_image.scss +29 -2
  51. package/styles/components/fs_text_area.scss +0 -13
  52. package/styles/components/fs_text_field.scss +1 -14
  53. package/styles/components/index.scss +2 -0
  54. package/styles/globals/overrides.scss +4 -0
  55. package/components/autocompletes/FSAutocompleteOrganisation.vue +0 -77
@@ -41,6 +41,7 @@ export default defineComponent({
41
41
  emits: ["update:dragstart", "update:dragend"],
42
42
  setup(props, { emit }) {
43
43
  let prevDragOverTarget: EventTarget | null = null;
44
+ const mobileGrabThreshold = 150;
44
45
 
45
46
  const draggedElementCopy = ref<HTMLElement|null>(null);
46
47
  const touchStartX = ref(0);
@@ -48,6 +49,8 @@ export default defineComponent({
48
49
  const touchEndX = ref(0);
49
50
  const touchEndY = ref(0);
50
51
 
52
+ const touchStartTime = ref(0);
53
+
51
54
  const classes = computed((): string[] => {
52
55
  const classNames = ["fs-draggable-item"];
53
56
  if (!props.disabled) {
@@ -60,27 +63,31 @@ export default defineComponent({
60
63
  if (props.disabled) {
61
64
  return;
62
65
  }
63
- event.preventDefault();
64
66
  const touch = event.touches[0];
65
67
  touchStartX.value = touch.clientX;
66
68
  touchStartY.value = touch.clientY;
67
69
 
68
- const dragged = (event.target as HTMLElement)?.closest(props.elementSelector) as HTMLElement;
69
- dragged.classList.add("fs-draggable-dragging");
70
- dragged.dataset.initialIndex = props.item?.index ?? props.item?.value;
70
+ touchStartTime.value = Date.now();
71
71
 
72
- draggedElementCopy.value = dragged.cloneNode(true) as HTMLElement;
73
- draggedElementCopy.value.style.position = "fixed";
74
- draggedElementCopy.value.style.left = `${touchStartX.value - 25}px`;
75
- draggedElementCopy.value.style.top = `${touchStartY.value - 25}px`;
76
- draggedElementCopy.value.style.zIndex = "1000";
77
- draggedElementCopy.value.style.pointerEvents = "none";
72
+ setTimeout(() => {
73
+ if(touchStartTime.value !== 0) {
74
+ const dragged = (event.target as HTMLElement)?.closest(props.elementSelector) as HTMLElement;
75
+ dragged.classList.add("fs-draggable-dragging");
76
+ dragged.classList.add("fs-draggable-dragging-grabbegin");
77
+ dragged.dataset.initialIndex = props.item?.index ?? props.item?.value;
78
+ event.preventDefault();
79
+ }
80
+ }, mobileGrabThreshold);
78
81
  };
79
82
 
80
83
  const onTouchMove = (event: TouchEvent) => {
81
84
  if (props.disabled) {
82
85
  return;
83
86
  }
87
+ if (Date.now() - touchStartTime.value < mobileGrabThreshold || touchStartTime.value === 0) {
88
+ touchStartTime.value = 0;
89
+ return;
90
+ }
84
91
  event.preventDefault();
85
92
  const touch = event.touches[0];
86
93
  touchEndX.value = touch.clientX;
@@ -122,8 +129,14 @@ export default defineComponent({
122
129
  if (props.disabled) {
123
130
  return;
124
131
  }
132
+
133
+ const dragged = (event.target as HTMLElement)?.closest(props.elementSelector) as HTMLElement;
134
+ if (dragged === null || touchStartTime.value === 0) {
135
+ return;
136
+ }
137
+
125
138
  event.preventDefault();
126
- const dragged = (event.target as HTMLElement)?.closest(props.elementSelector);
139
+
127
140
  if (draggedElementCopy.value) {
128
141
  draggedElementCopy.value.remove();
129
142
  draggedElementCopy.value = null;
@@ -131,7 +144,7 @@ export default defineComponent({
131
144
 
132
145
  const dropTarget = document.elementFromPoint(touchEndX.value, touchEndY.value);
133
146
  const dragEndEvent = new Event("dragend");
134
- Object.defineProperty(dragEndEvent, "srcElement", {
147
+ Object.defineProperty(dragEndEvent, "target", {
135
148
  get: function () { return event.target; }
136
149
  });
137
150
  emit("update:dragend", dragEndEvent, dragged);
@@ -140,7 +153,7 @@ export default defineComponent({
140
153
  bubbles: true,
141
154
  cancelable: true,
142
155
  });
143
- dropEvent.dataTransfer?.setData("text/plain", JSON.stringify(props.item));
156
+ dragged!.dataset.item = JSON.stringify(props.item);
144
157
  dropTarget?.dispatchEvent(dropEvent);
145
158
 
146
159
  touchStartX.value = 0;
@@ -3,7 +3,9 @@
3
3
  :closeOnContentClick="false"
4
4
  v-model="expanded"
5
5
  >
6
- <template #activator="{ props }">
6
+ <template
7
+ #activator="{ props }"
8
+ >
7
9
  <FSChip
8
10
  class="fs-filter-button"
9
11
  variant="standard"
@@ -57,8 +59,12 @@
57
59
  :variant="getVariant(filter)"
58
60
  @click="() => onToggle(filter)"
59
61
  >
60
- <template #default>
61
- <slot v-bind="{ filter }" />
62
+ <template
63
+ #default
64
+ >
65
+ <slot
66
+ v-bind="{ filter }"
67
+ />
62
68
  </template>
63
69
  </FSChip>
64
70
  </FSCol>
@@ -117,7 +123,7 @@ export default defineComponent({
117
123
 
118
124
  const label = computed((): string | null => {
119
125
  if (props.header.text) {
120
- if (props.filters) {
126
+ if (props.filters) {
121
127
  const hidden = props.filters.filter(f => f.hidden).length;
122
128
  if (hidden > 0) {
123
129
  return $tr("ui.data-table.some-filters-visible", "{0}: {1} visible", props.header.text, (props.filters.length - hidden).toString());
@@ -2,7 +2,9 @@
2
2
  <v-menu
3
3
  v-model="expanded"
4
4
  >
5
- <template #activator="{ props }">
5
+ <template
6
+ #activator="{ props }"
7
+ >
6
8
  <FSButton
7
9
  class="fs-header-button"
8
10
  icon="mdi-dots-vertical"
@@ -3,7 +3,9 @@
3
3
  :closeOnContentClick="false"
4
4
  v-model="expanded"
5
5
  >
6
- <template #activator="{ props }">
6
+ <template
7
+ #activator="{ props }"
8
+ >
7
9
  <FSChip
8
10
  prependIcon="mdi-eye-off-outline"
9
11
  :color="ColorEnum.Light"
@@ -7,7 +7,7 @@ export const useAutocomplete = <TInfos>(
7
7
  entities: Ref<TInfos[]>,
8
8
  filters: (() => any)[],
9
9
  emit: (event: "update:modelValue", value: string[] | string | null) => void,
10
- customFetch: (search: string | null) => Promise<any>,
10
+ customFetch: (search: string) => Promise<any>,
11
11
  customUpdate: ((item: TInfos[] | TInfos | null) => void) | null = null,
12
12
  toId: (item: TInfos) => string | null = (item: TInfos) => (item as any).id,
13
13
  toText: (item: TInfos) => string | null = (item: TInfos) => (item as any).label,
@@ -18,13 +18,12 @@ export const useAutocomplete = <TInfos>(
18
18
  ) => {
19
19
  const { debounce } = useDebounce();
20
20
 
21
- const search = ref<string | null>(null);
21
+ const search = ref<string>("");
22
22
  const entitiesLength = ref(0);
23
23
  const init = ref(true);
24
24
 
25
25
  const toggleSet = computed((): boolean => {
26
- console.log(allowToggleSet, entitiesLength.value, breakpointToggleSet);
27
- return allowToggleSet && entitiesLength.value < breakpointToggleSet;
26
+ return allowToggleSet && entitiesLength.value <= breakpointToggleSet;
28
27
  });
29
28
 
30
29
  const debouncedFetch = () => debounce(() => customFetch(search.value), debounceInterval);
@@ -52,9 +51,9 @@ export const useAutocomplete = <TInfos>(
52
51
 
53
52
  watch(search, (newValue, oldValue) => {
54
53
  if (newValue !== oldValue) {
55
- const found = entities.value.map(e => toText(e)).includes(search.value);
56
- if (!found && (!search.value || !search.value.length || search.value.length > 2)) {
57
- if (fetchOnSearch) {
54
+ if (fetchOnSearch) {
55
+ const found = entities.value.map(e => toText(e)).includes(search.value);
56
+ if (!found && (!search.value || !search.value.length || search.value.length > 2)) {
58
57
  debouncedFetch();
59
58
  }
60
59
  }
@@ -10,34 +10,22 @@ export const useSlots = () => {
10
10
  // Directive wrapper (v-for, v-if)
11
11
  case "symbol":
12
12
  switch (slot()[0].type) {
13
- case Symbol.for("v-fgt"):
14
- return slot()[0].children;
15
- case Symbol.for("v-cmt"):
16
- return slot();
13
+ case Symbol.for("v-fgt"): return slot()[0].children;
14
+ case Symbol.for("v-cmt"): return slot();
15
+ default: return slot();
17
16
  }
18
17
  // Custom component
19
- case "object":
20
- return slot();
18
+ case "object": return slot();
21
19
  // Pre-existing component
22
- case "string":
23
- return slot();
20
+ case "string": return slot();
24
21
  }
25
22
  return slot();
26
23
  }
27
24
  return null;
28
25
  };
29
26
 
30
- const getFirstChild = (name: string | undefined = undefined): any => {
31
- const children = getChildren(name);
32
- if (children != null) {
33
- return children[0];
34
- }
35
- return null;
36
- };
37
-
38
27
  return {
39
28
  slots: { ...useVueSlots() } as { [label: string]: Slot<any> },
40
- getChildren,
41
- getFirstChild
29
+ getChildren
42
30
  };
43
31
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@dative-gpi/foundation-shared-components",
3
3
  "sideEffects": false,
4
- "version": "0.0.87",
4
+ "version": "0.0.89",
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": "0.0.87",
14
- "@dative-gpi/foundation-shared-services": "0.0.87",
13
+ "@dative-gpi/foundation-shared-domain": "0.0.89",
14
+ "@dative-gpi/foundation-shared-services": "0.0.89",
15
15
  "@fontsource/montserrat": "^5.0.16",
16
16
  "@lexical/clipboard": "^0.12.5",
17
17
  "@lexical/history": "^0.12.5",
@@ -32,5 +32,5 @@
32
32
  "sass": "^1.69.5",
33
33
  "sass-loader": "^13.3.2"
34
34
  },
35
- "gitHead": "ae864c1fac5a392f6fcc8e77be26fe1613eb1165"
35
+ "gitHead": "9bacb675fe5a6db96fbb13de6baeb94ac288fcff"
36
36
  }
@@ -0,0 +1,12 @@
1
+ .fs-base-field-label {
2
+ color: var(--fs-base-field-color);
3
+ }
4
+
5
+ .fs-base-field-messages {
6
+ align-self: stretch;
7
+ color: var(--fs-base-field-error-color);
8
+ }
9
+
10
+ .fs-base-field-description {
11
+ color: var(--fs-base-field-color);
12
+ }
@@ -11,6 +11,16 @@
11
11
  margin: 0px !important;
12
12
  }
13
13
 
14
+ .v-overlay__content:has(.fs-dialog) {
15
+ min-width: 35vw !important;
16
+ max-width: 90vw !important;
17
+ max-height: 90vh !important;
18
+
19
+ &.v-overlay__content:has(.fs-dialog-mobile) {
20
+ max-width: 100vw !important;
21
+ }
22
+ }
23
+
14
24
  .fs-dialog {
15
25
  position: relative;
16
26
  min-width: 35vw !important;
@@ -18,8 +28,6 @@
18
28
  max-height: 90vh !important;
19
29
 
20
30
  &.fs-dialog-mobile {
21
- border-top-left-radius: 4px;
22
- border-top-right-radius: 4px;
23
31
  max-width: 100vw !important;
24
32
  }
25
33
  }
@@ -0,0 +1,11 @@
1
+ .v-overlay__content:has(.fs-dialog-menu) {
2
+ width: fit-content !important;
3
+ max-height: 60vh !important;
4
+ justify-content: center;
5
+ align-items: center;
6
+ }
7
+
8
+ .fs-dialog-menu {
9
+ max-width: calc(100vw - 40px) !important;
10
+ max-height: 60vh !important;
11
+ }
@@ -12,6 +12,18 @@
12
12
  filter: blur(1px);
13
13
  }
14
14
 
15
+ .fs-draggable-dragging-grabbegin {
16
+ animation: tilt-shaking 0.1s 2;
17
+ }
18
+
19
+ @keyframes tilt-shaking {
20
+ 0% { transform: rotate(0deg); }
21
+ 25% { transform: rotate(2deg); }
22
+ 50% { transform: rotate(0deg); }
23
+ 75% { transform: rotate(-2deg); }
24
+ 100% { transform: rotate(0deg); }
25
+ }
26
+
15
27
  .fs-dropzone-include {
16
28
  transition: all 0.28s cubic-bezier(0.4, 0, 0.2, 1);
17
29
  filter: brightness(0.85) contrast(1.1);
@@ -1,3 +1,30 @@
1
- .fs-edit-image {
2
- border: 1px dashed var(--fs-edit-image-border-color);
1
+ .fs-edit-image-overline {
2
+ color: var(--fs-edit-image-overline-text-color) !important;
3
+ }
4
+
5
+ .fs-edit-image-hidden-button {
6
+ display: none;
7
+ }
8
+
9
+ .fs-edit-image-full {
10
+ position: relative;
11
+
12
+ .fs-edit-image-full-toolbar {
13
+ position: absolute;
14
+ right: 0;
15
+ top: 0;
16
+ }
17
+ }
18
+
19
+ .fs-edit-image-label {
20
+ color: var(--fs-edit-image-color);
21
+ }
22
+
23
+ .fs-edit-image-messages {
24
+ align-self: stretch;
25
+ color: var(--fs-edit-image-error-color);
26
+ }
27
+
28
+ .fs-edit-image-description {
29
+ color: var(--fs-edit-image-color);
3
30
  }
@@ -72,17 +72,4 @@
72
72
  }
73
73
  }
74
74
  }
75
- }
76
-
77
- .fs-text-area-label {
78
- color: var(--fs-text-area-color);
79
- }
80
-
81
- .fs-text-area-messages {
82
- align-self: stretch;
83
- color: var(--fs-text-area-error-color);
84
- }
85
-
86
- .fs-text-area-description {
87
- color: var(--fs-text-area-color);
88
75
  }
@@ -11,20 +11,7 @@
11
11
  }
12
12
 
13
13
  & > .v-field__field > .v-field__input {
14
+ cursor: var(--fs-text-field-cursor) !important;
14
15
  color: var(--fs-text-field-color);
15
- cursor: var(--fs-select-field-cursor) !important;
16
16
  }
17
- }
18
-
19
- .fs-text-field-label {
20
- color: var(--fs-text-field-color);
21
- }
22
-
23
- .fs-text-field-messages {
24
- align-self: stretch;
25
- color: var(--fs-text-field-error-color);
26
- }
27
-
28
- .fs-text-field-description {
29
- color: var(--fs-text-field-color);
30
17
  }
@@ -1,5 +1,6 @@
1
1
  @import "fs_accordion_panel.scss";
2
2
  @import "fs_autocomplete_field.scss";
3
+ @import "fs_base_field.scss";
3
4
  @import "fs_breadcrumbs.scss";
4
5
  @import "fs_button.scss";
5
6
  @import "fs_calendar.scss";
@@ -16,6 +17,7 @@
16
17
  @import "fs_data_table.scss";
17
18
  @import "fs_data_iterator_item.scss";
18
19
  @import "fs_date_field.scss";
20
+ @import "fs_dialog_menu.scss";
19
21
  @import "fs_dialog.scss";
20
22
  @import "fs_divider.scss";
21
23
  @import "fs_draggable.scss";
@@ -99,6 +99,10 @@
99
99
  display: none !important;
100
100
  }
101
101
 
102
+ .v-overlay__content {
103
+ min-width: fit-content !important;
104
+ }
105
+
102
106
  // No up / down buttons in input field of type number
103
107
  input[type=number] {
104
108
  -moz-appearance: textfield;
@@ -1,77 +0,0 @@
1
- <template>
2
- <FSAutocompleteField
3
- :toggleSet="!$props.toggleSetDisabled && toggleSet"
4
- :loading="loading"
5
- :items="organisations"
6
- :modelValue="$props.modelValue"
7
- @update:modelValue="onUpdate"
8
- v-model:search="search"
9
- v-bind="$attrs" />
10
- </template>
11
- <script lang="ts">
12
- import { PropType, computed, defineComponent, watch } from 'vue'
13
- import _ from 'lodash';
14
-
15
- import { OrganisationFilters } from '@dative-gpi/foundation-shared-domain/models';
16
- import { useOrganisations } from '@dative-gpi/foundation-shared-services/composables';
17
-
18
- import { useAutocomplete } from '../../composables';
19
-
20
- import FSAutocompleteField from '../fields/FSAutocompleteField.vue'
21
-
22
- export default defineComponent({
23
- name: 'FSAutocompleteOrganisation',
24
- components: {
25
- FSAutocompleteField
26
- },
27
- props: {
28
- organisationFilters: {
29
- type: Object as PropType<OrganisationFilters>,
30
- required: false,
31
- default: null
32
- },
33
- modelValue: {
34
- type: [Array, String] as PropType<string[] | string | null>,
35
- required: false,
36
- default: null
37
- },
38
- toggleSetDisabled: {
39
- type: Boolean,
40
- required: false,
41
- default: false
42
- }
43
- },
44
- emit: ['update:modelValue'],
45
- setup(props, { emit }) {
46
- const { entities: organisations, fetching: fetchingOrganisations, getMany: getManyOrganisations } = useOrganisations();
47
-
48
- const innerFetch = (search: string | null) => {
49
- return getManyOrganisations({ ...props.organisationFilters, search: search ?? undefined });
50
- };
51
-
52
- const { toggleSet, search, init, onUpdate } = useAutocomplete(
53
- organisations,
54
- [() => props.organisationFilters],
55
- emit,
56
- innerFetch
57
- );
58
-
59
- const isSelected = (id: any) => {
60
- return props.modelValue?.includes(id);
61
- }
62
-
63
- const loading = computed((): boolean => {
64
- return init.value && fetchingOrganisations.value;
65
- });
66
-
67
- return {
68
- organisations,
69
- toggleSet,
70
- loading,
71
- search,
72
- isSelected,
73
- onUpdate
74
- }
75
- }
76
- })
77
- </script>