@dative-gpi/foundation-shared-components 0.0.8 → 0.0.10

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 (140) hide show
  1. package/{models/FSButtons.ts → aliases/FSButton.ts} +24 -21
  2. package/aliases/index.ts +1 -0
  3. package/components/FSAutocompleteField.vue +207 -0
  4. package/components/FSBadge.vue +38 -0
  5. package/components/FSBreadcrumbs.vue +49 -55
  6. package/components/FSButton.vue +116 -101
  7. package/components/FSCalendar.vue +52 -39
  8. package/components/FSCalendarTwin.vue +120 -102
  9. package/components/FSCard.vue +35 -21
  10. package/components/FSCarousel.vue +63 -0
  11. package/components/FSCheckbox.vue +111 -103
  12. package/components/FSChip.vue +140 -0
  13. package/components/FSClock.vue +149 -15
  14. package/components/FSCol.vue +104 -98
  15. package/components/FSColor.vue +61 -64
  16. package/components/FSColorIcon.vue +67 -0
  17. package/components/FSContainer.vue +64 -0
  18. package/components/FSDateField.vue +211 -0
  19. package/components/FSDateRangeField.vue +225 -0
  20. package/components/FSDateTimeField.vue +257 -0
  21. package/components/FSDateTimeRangeField.vue +286 -0
  22. package/components/FSDialog.vue +103 -0
  23. package/components/FSDivider.vue +39 -0
  24. package/components/FSFadeOut.vue +49 -59
  25. package/components/FSFileButton.vue +245 -0
  26. package/components/FSHeaderButton.vue +17 -0
  27. package/components/FSIcon.vue +23 -23
  28. package/components/FSIconField.vue +232 -0
  29. package/components/FSImage.vue +142 -0
  30. package/components/FSLoadTile.vue +93 -0
  31. package/components/FSNumberField.vue +51 -53
  32. package/components/FSPasswordField.vue +99 -101
  33. package/components/FSRadio.vue +107 -109
  34. package/components/FSRadioGroup.vue +55 -57
  35. package/components/FSRemoveDialog.vue +123 -0
  36. package/components/FSRichTextField.vue +26 -33
  37. package/components/FSRow.vue +110 -104
  38. package/components/FSSearchField.vue +35 -27
  39. package/components/FSSelectField.vue +188 -0
  40. package/components/FSSlideGroup.vue +45 -49
  41. package/components/FSSlider.vue +31 -33
  42. package/components/FSSpan.vue +53 -37
  43. package/components/FSSubmitDialog.vue +165 -0
  44. package/components/FSSwitch.vue +110 -109
  45. package/components/FSTab.vue +61 -61
  46. package/components/FSTabs.vue +53 -55
  47. package/components/FSTag.vue +88 -84
  48. package/components/FSTagField.vue +32 -36
  49. package/components/FSTagGroup.vue +38 -45
  50. package/components/FSText.vue +74 -64
  51. package/components/FSTextArea.vue +187 -185
  52. package/components/FSTextField.vue +18 -20
  53. package/components/FSTile.vue +90 -0
  54. package/components/FSToggleSet.vue +282 -0
  55. package/components/FSTooltip.vue +21 -0
  56. package/components/FSWindow.vue +26 -16
  57. package/components/FSWrapGroup.vue +44 -47
  58. package/components/deviceOrganisations/FSConnectivity.vue +114 -0
  59. package/components/deviceOrganisations/FSStatus.vue +117 -0
  60. package/components/deviceOrganisations/FSStatusesCarousel.vue +105 -0
  61. package/components/deviceOrganisations/FSStatusesRow.vue +66 -0
  62. package/components/deviceOrganisations/FSWorstAlert.vue +165 -0
  63. package/components/lists/FSDataIteratorGroup.vue +7 -0
  64. package/components/lists/FSDataIteratorItem.vue +103 -0
  65. package/components/lists/FSDataTableUI.vue +982 -0
  66. package/components/lists/FSFilterButton.vue +177 -0
  67. package/components/lists/FSHeaderButton.vue +99 -0
  68. package/components/lists/FSHiddenButton.vue +81 -0
  69. package/components/tiles/FSDeviceOrganisationTileUI.vue +232 -0
  70. package/components/tiles/FSGroupTileUI.vue +192 -0
  71. package/composables/index.ts +1 -1
  72. package/composables/useBreakpoints.ts +23 -4
  73. package/composables/useColors.ts +53 -23
  74. package/composables/useSlots.ts +43 -0
  75. package/index.ts +6 -0
  76. package/models/breadcrumbs.ts +8 -0
  77. package/models/colors.ts +17 -0
  78. package/models/deviceAlerts.ts +10 -0
  79. package/models/deviceConnectivities.ts +11 -0
  80. package/models/deviceStatuses.ts +16 -0
  81. package/models/index.ts +9 -0
  82. package/models/modelStatuses.ts +11 -0
  83. package/models/rules.ts +50 -0
  84. package/models/tables.ts +33 -0
  85. package/models/toggleSets.ts +7 -0
  86. package/package.json +6 -4
  87. package/plugins/colorPlugin.ts +2 -2
  88. package/shims-plugin.d.ts +1 -1
  89. package/styles/components/fs_autocomplete_field.scss +123 -0
  90. package/styles/components/fs_button.scss +4 -6
  91. package/styles/components/fs_calendar.scss +24 -1
  92. package/styles/components/fs_card.scss +2 -5
  93. package/styles/components/fs_carousel.scss +4 -0
  94. package/styles/components/fs_chip.scss +33 -0
  95. package/styles/components/fs_clock.scss +43 -0
  96. package/styles/components/fs_col.scss +2 -0
  97. package/styles/components/fs_color_icon.scss +37 -0
  98. package/styles/components/fs_container.scss +16 -0
  99. package/styles/components/fs_data_iterator_item.scss +19 -0
  100. package/styles/components/fs_data_table.scss +97 -0
  101. package/styles/components/fs_date_field.scss +8 -0
  102. package/styles/components/fs_dialog.scss +30 -0
  103. package/styles/components/fs_divider.scss +5 -0
  104. package/styles/components/fs_fade_out.scss +10 -2
  105. package/styles/components/fs_filter_button.scss +21 -0
  106. package/styles/components/fs_header_button.scss +4 -0
  107. package/styles/components/fs_hidden_button.scss +12 -0
  108. package/styles/components/fs_icon.scss +19 -3
  109. package/styles/components/fs_icon_field.scss +12 -0
  110. package/styles/components/fs_image.scss +7 -0
  111. package/styles/components/fs_load_tile.scss +49 -0
  112. package/styles/components/fs_password_field.scss +2 -2
  113. package/styles/components/fs_row.scss +4 -1
  114. package/styles/components/fs_select_field.scss +71 -0
  115. package/styles/components/fs_slide_group.scss +6 -0
  116. package/styles/components/fs_slider.scss +29 -9
  117. package/styles/components/fs_span.scss +8 -0
  118. package/styles/components/fs_submit_dialog.scss +9 -0
  119. package/styles/components/fs_tabs.scss +4 -0
  120. package/styles/components/fs_tag_field.scss +0 -11
  121. package/styles/components/fs_text_field.scss +23 -15
  122. package/styles/components/fs_tile.scss +33 -0
  123. package/styles/components/fs_tooltip.scss +5 -0
  124. package/styles/components/fs_wrap_group.scss +7 -8
  125. package/styles/components/index.scss +22 -1
  126. package/styles/globals/breakpoints.scss +7 -0
  127. package/styles/globals/overrides.scss +20 -7
  128. package/styles/globals/text_fonts.scss +8 -8
  129. package/themes/default.ts +1 -11
  130. package/utils/css.ts +11 -0
  131. package/utils/icons.ts +75416 -0
  132. package/utils/index.ts +5 -1
  133. package/utils/levenshtein.ts +97 -0
  134. package/utils/sort.ts +9 -0
  135. package/components/FSDatePicker.vue +0 -226
  136. package/composables/useDates.ts +0 -39
  137. package/models/FSTags.ts +0 -8
  138. package/models/FSTextFields.ts +0 -23
  139. package/styles/components/fs_date_picker.scss +0 -0
  140. /package/utils/{FSRichTextField.ts → lexical.ts} +0 -0
@@ -0,0 +1,245 @@
1
+ <template>
2
+ <div>
3
+ <v-btn
4
+ v-if="!['icon'].includes($props.variant)"
5
+ :ripple="false"
6
+ :style="style"
7
+ :class="classes"
8
+ :disabled="!$props.editable"
9
+ @click="onClick"
10
+ v-bind="$attrs"
11
+ >
12
+ <FSRow
13
+ align="center-center"
14
+ :wrap="false"
15
+ >
16
+ <slot name="prepend" v-bind="{ color: $props.color, colors }">
17
+ <FSIcon
18
+ v-if="$props.prependIcon"
19
+ size="l"
20
+ >
21
+ {{ $props.prependIcon }}
22
+ </FSIcon>
23
+ </slot>
24
+ <slot v-bind="{ color: $props.color, colors }">
25
+ <FSSpan
26
+ v-if="$props.label"
27
+ font="text-body"
28
+ >
29
+ {{ $props.label }}
30
+ </FSSpan>
31
+ </slot>
32
+ <slot name="append" v-bind="{ color: $props.color, colors }">
33
+ <FSIcon
34
+ v-if="$props.appendIcon"
35
+ size="l"
36
+ >
37
+ {{ $props.appendIcon }}
38
+ </FSIcon>
39
+ </slot>
40
+ </FSRow>
41
+ </v-btn>
42
+ <FSRow
43
+ v-else-if="$props.icon"
44
+ width="hug"
45
+ :style="style"
46
+ :class="classes"
47
+ v-bind="$attrs"
48
+ >
49
+ <FSIcon
50
+ size="l"
51
+ >
52
+ {{ $props.icon }}
53
+ </FSIcon>
54
+ </FSRow>
55
+ <form>
56
+ <input
57
+ v-show="false"
58
+ type="file"
59
+ ref="input"
60
+ :accept="$props.accept"
61
+ @input="onInput"
62
+ />
63
+ </form>
64
+ </div>
65
+ </template>
66
+
67
+ <script lang="ts">
68
+ import { computed, defineComponent, PropType, ref } from "vue";
69
+
70
+ import { useColors, useSlots } from "@dative-gpi/foundation-shared-components/composables";
71
+ import { ColorBase, ColorEnum } from "@dative-gpi/foundation-shared-components/models";
72
+
73
+ import FSSpan from "./FSSpan.vue";
74
+ import FSIcon from "./FSIcon.vue";
75
+ import FSRow from "./FSRow.vue";
76
+
77
+ export default defineComponent({
78
+ name: "FSButtonFile",
79
+ components: {
80
+ FSSpan,
81
+ FSIcon,
82
+ FSRow
83
+ },
84
+ props: {
85
+ accept: {
86
+ type: String,
87
+ required: false,
88
+ default: "",
89
+ },
90
+ readFile: {
91
+ type: Boolean,
92
+ required: false,
93
+ default: true,
94
+ },
95
+ prependIcon: {
96
+ type: String,
97
+ required: false,
98
+ default: null
99
+ },
100
+ label: {
101
+ type: String,
102
+ required: false,
103
+ default: null
104
+ },
105
+ appendIcon: {
106
+ type: String,
107
+ required: false,
108
+ default: null
109
+ },
110
+ icon: {
111
+ type: String,
112
+ required: false,
113
+ default: null
114
+ },
115
+ variant: {
116
+ type: String as PropType<"standard" | "full" | "icon">,
117
+ required: false,
118
+ default: "standard"
119
+ },
120
+ color: {
121
+ type: String as PropType<ColorBase>,
122
+ required: false,
123
+ default: ColorEnum.Dark
124
+ },
125
+ editable: {
126
+ type: Boolean,
127
+ required: false,
128
+ default: true
129
+ }
130
+ },
131
+ emits: ["update:modelValue"],
132
+ setup(props, { emit }) {
133
+ const { slots } = useSlots();
134
+
135
+ const textColors = computed(() => useColors().getContrasts(props.color));
136
+ const colors = computed(() => useColors().getColors(props.color));
137
+ const lights = useColors().getColors(ColorEnum.Light);
138
+
139
+ const input = ref(null);
140
+
141
+ const isEmpty = computed(() => {
142
+ return !slots.default && !props.label;
143
+ });
144
+
145
+ const style = computed((): {[code: string]: string} & Partial<CSSStyleDeclaration> => {
146
+ if (!props.editable) {
147
+ switch (props.variant) {
148
+ case "standard":
149
+ case "full": return {
150
+ "--fs-button-padding": !isEmpty.value ? "0 16px" : "0",
151
+ "--fs-button-background-color": lights.base,
152
+ "--fs-button-border-color": lights.dark,
153
+ "--fs-button-color": lights.dark
154
+ }
155
+ case "icon": return {
156
+ "--fs-button-color": lights.dark
157
+ }
158
+ }
159
+ }
160
+ switch (props.variant) {
161
+ case "standard": return {
162
+ "--fs-button-padding" : !isEmpty.value ? "0 16px" : "0",
163
+ "--fs-button-background-color" : colors.value.light,
164
+ "--fs-button-border-color" : colors.value.base,
165
+ "--fs-button-color" : textColors.value.base,
166
+ "--fs-button-hover-background-color" : colors.value.base,
167
+ "--fs-button-hover-border-color" : colors.value.base,
168
+ "--fs-button-hover-color" : textColors.value.light,
169
+ "--fs-button-active-background-color": colors.value.dark,
170
+ "--fs-button-active-border-color" : colors.value.dark,
171
+ "--fs-button-active-color" : textColors.value.light
172
+ };
173
+ case "full": return {
174
+ "--fs-button-padding" : !isEmpty.value ? "0 16px" : "0",
175
+ "--fs-button-background-color" : colors.value.base,
176
+ "--fs-button-border-color" : colors.value.base,
177
+ "--fs-button-color" : textColors.value.light,
178
+ "--fs-button-hover-background-color" : colors.value.base,
179
+ "--fs-button-hover-border-color" : colors.value.base,
180
+ "--fs-button-hover-color" : textColors.value.light,
181
+ "--fs-button-active-background-color": colors.value.dark,
182
+ "--fs-button-active-border-color" : colors.value.dark,
183
+ "--fs-button-active-color" : textColors.value.light
184
+ };
185
+ case "icon": return {
186
+ "--fs-button-color" : textColors.value.base,
187
+ "--fs-button-hover-color": textColors.value.dark
188
+ };
189
+ }
190
+ });
191
+
192
+ const classes = computed((): string[] => {
193
+ const classNames = [];
194
+ if (!props.editable) {
195
+ classNames.push("fs-button--disabled");
196
+ }
197
+ switch (props.variant) {
198
+ case "icon":
199
+ classNames.push("fs-button-icon");
200
+ break;
201
+ default:
202
+ classNames.push("fs-button");
203
+ break;
204
+ }
205
+ return classNames;
206
+ });
207
+
208
+ const clear = () => {
209
+ input.value!.form && input.value!.form.reset();
210
+ };
211
+
212
+ const onClick = () => {
213
+ input.value!.click();
214
+ }
215
+
216
+ const onInput = () => {
217
+ const file = input.value!.files && input.value!.files[0];
218
+ if (!file) {
219
+ return;
220
+ }
221
+ if (!props.readFile) {
222
+ emit("update:modelValue", file);
223
+ clear();
224
+ }
225
+ else {
226
+ const reader = new FileReader();
227
+ reader.addEventListener("load", (fileEv) => {
228
+ emit("update:modelValue", fileEv.target && fileEv.target.result);
229
+ clear();
230
+ });
231
+ reader.readAsDataURL(file);
232
+ }
233
+ };
234
+
235
+ return {
236
+ colors,
237
+ style,
238
+ classes,
239
+ input,
240
+ onClick,
241
+ onInput
242
+ };
243
+ }
244
+ });
245
+ </script>
@@ -0,0 +1,17 @@
1
+ <template>
2
+
3
+ </template>
4
+
5
+ <script lang="ts">
6
+ import { defineComponent } from "vue";
7
+
8
+ export default defineComponent({
9
+ name: "FSHeaderButton",
10
+ props: {
11
+ },
12
+ setup(props) {
13
+ return {
14
+ };
15
+ }
16
+ });
17
+ </script>
@@ -1,32 +1,32 @@
1
1
  <template>
2
- <v-icon
3
- :class="classes"
4
- v-bind="$attrs"
5
- >
6
- <slot />
7
- </v-icon>
2
+ <v-icon
3
+ :class="classes"
4
+ v-bind="$attrs"
5
+ >
6
+ <slot />
7
+ </v-icon>
8
8
  </template>
9
9
 
10
10
  <script lang="ts">
11
- import { computed, defineComponent, PropType, toRefs } from "vue";
11
+ import { computed, defineComponent, PropType } from "vue";
12
12
 
13
13
  export default defineComponent({
14
- name: "FSIcon",
15
- props: {
16
- size: {
17
- type: String as PropType<"s" | "m" | "l">,
18
- required: false,
19
- default: "m"
20
- }
21
- },
22
- setup(props) {
23
- const { size } = toRefs(props);
24
-
25
- const classes = computed((): string[] => ["fs-icon", `fs-icon-${size.value}`]);
26
-
27
- return {
28
- classes
29
- };
14
+ name: "FSIcon",
15
+ props: {
16
+ size: {
17
+ type: String as PropType<"s" | "m" | "l">,
18
+ required: false,
19
+ default: "m"
30
20
  }
21
+ },
22
+ setup(props) {
23
+ const classes = computed((): string[] => {
24
+ return [`fs-icon-${props.size}`];
25
+ });
26
+
27
+ return {
28
+ classes
29
+ };
30
+ }
31
31
  });
32
32
  </script>
@@ -0,0 +1,232 @@
1
+ <template>
2
+ <FSCol>
3
+ <FSTextField
4
+ :label="$props.label"
5
+ :description="$props.description"
6
+ :hideHeader="$props.hideHeader"
7
+ :required="$props.required"
8
+ :editable="$props.editable"
9
+ :error="messages.length > 0"
10
+ v-model="innerValue"
11
+ v-bind="$attrs"
12
+ >
13
+ <template v-if="!$props.hideHeader" #label>
14
+ <slot name="label">
15
+ <FSRow :wrap="false">
16
+ <FSSpan
17
+ v-if="$props.label"
18
+ class="fs-icon-field-label"
19
+ font="text-overline"
20
+ :style="style"
21
+ >
22
+ {{ $props.label }}
23
+ </FSSpan>
24
+ <FSSpan
25
+ v-if="$props.label && $props.required"
26
+ class="fs-icon-field-label"
27
+ style="margin-left: -8px;"
28
+ font="text-overline"
29
+ :ellipsis="false"
30
+ :style="style"
31
+ >
32
+ *
33
+ </FSSpan>
34
+ <v-spacer style="min-width: 40px;" />
35
+ <FSSpan
36
+ v-if="messages.length > 0"
37
+ class="fs-icon-field-messages"
38
+ font="text-overline"
39
+ :style="style"
40
+ >
41
+ {{ messages.join(", ") }}
42
+ </FSSpan>
43
+ </FSRow>
44
+ </slot>
45
+ </template>
46
+ <template #append-inner>
47
+ <FSIcon
48
+ v-if="$props.modelValue"
49
+ size="l"
50
+ >
51
+ {{ $props.modelValue }}
52
+ </FSIcon>
53
+ </template>
54
+ </FSTextField>
55
+ <FSToggleSet
56
+ class="fs-icon-field-set"
57
+ variant="slide"
58
+ :values="icons"
59
+ :required="$props.required"
60
+ :editable="$props.editable"
61
+ :buttonColor="$props.buttonColor"
62
+ :activeColor="$props.activeColor"
63
+ :modelValue="$props.modelValue"
64
+ @update:modelValue="(value) => $emit('update:modelValue', value)"
65
+ />
66
+ </FSCol>
67
+ </template>
68
+
69
+ <script lang="ts">
70
+ import { computed, defineComponent, PropType, ref } from "vue";
71
+
72
+ import { Icons, sortByLevenshteinDistance } from "@dative-gpi/foundation-shared-components/utils";
73
+ import { ColorBase, ColorEnum } from "@dative-gpi/foundation-shared-components/models";
74
+ import { useColors } from "@dative-gpi/foundation-shared-components/composables";
75
+ import { FSToggle } from "@dative-gpi/foundation-shared-components/models";
76
+
77
+ import FSTextField from "./FSTextField.vue";
78
+ import FSToggleSet from "./FSToggleSet.vue";
79
+ import FSIcon from "./FSIcon.vue";
80
+ import FSCol from "./FSCol.vue";
81
+
82
+ export default defineComponent({
83
+ name: "FSIconField",
84
+ components: {
85
+ FSTextField,
86
+ FSToggleSet,
87
+ FSIcon,
88
+ FSCol,
89
+ },
90
+ props: {
91
+ label: {
92
+ type: String,
93
+ required: false,
94
+ default: null
95
+ },
96
+ description: {
97
+ type: String,
98
+ required: false,
99
+ default: null
100
+ },
101
+ preSelection: {
102
+ type: Array as PropType<string[]>,
103
+ required: false,
104
+ default: null
105
+ },
106
+ buttonVariant: {
107
+ type: String as PropType<"standard" | "full" | "icon">,
108
+ required: false,
109
+ default: "standard"
110
+ },
111
+ activeVariant: {
112
+ type: String as PropType<"standard" | "full" | "icon">,
113
+ required: false,
114
+ default: "standard"
115
+ },
116
+ modelValue: {
117
+ type: String,
118
+ required: false,
119
+ default: null
120
+ },
121
+ buttonColor: {
122
+ type: String as PropType<ColorBase>,
123
+ required: false,
124
+ default: ColorEnum.Light
125
+ },
126
+ activeColor: {
127
+ type: String as PropType<ColorBase>,
128
+ required: false,
129
+ default: ColorEnum.Primary
130
+ },
131
+ hideHeader: {
132
+ type: Boolean,
133
+ required: false,
134
+ default: false
135
+ },
136
+ required: {
137
+ type: Boolean,
138
+ required: false,
139
+ default: false
140
+ },
141
+ rules: {
142
+ type: Array as PropType<Function[]>,
143
+ required: false,
144
+ default: () => []
145
+ },
146
+ editable: {
147
+ type: Boolean,
148
+ required: false,
149
+ default: true
150
+ }
151
+ },
152
+ emits: ["update:modelValue"],
153
+ setup(props) {
154
+ const errors = useColors().getColors(ColorEnum.Error);
155
+ const lights = useColors().getColors(ColorEnum.Light);
156
+ const darks = useColors().getColors(ColorEnum.Dark);
157
+
158
+ const innerValue = ref(null);
159
+
160
+ const style = computed((): {[code: string]: string} & Partial<CSSStyleDeclaration> => {
161
+ if (!props.editable) {
162
+ return {
163
+ "--fs-icon-field-color": lights.dark
164
+ };
165
+ }
166
+ return {
167
+ "--fs-icon-field-color" : darks.base,
168
+ "--fs-icon-field-error-color": errors.base
169
+ };
170
+ });
171
+
172
+ const messages = computed((): string[] => {
173
+ const messages = [];
174
+ for (const rule of props.rules) {
175
+ const message = rule(props.modelValue);
176
+ if (typeof(message) === "string") {
177
+ messages.push(message);
178
+ }
179
+ }
180
+ return messages;
181
+ });
182
+
183
+ const icons = computed((): FSToggle[] => {
184
+ const innerIcons: FSToggle[] = [];
185
+ if (!innerValue.value || innerValue.value.length < 3) {
186
+ if (props.preSelection && props.preSelection.length) {
187
+ innerIcons.push(...props.preSelection.map((icon: string) => ({
188
+ id: icon,
189
+ prependIcon: icon
190
+ })));
191
+ }
192
+ else {
193
+ innerIcons.push(...Icons.slice(0, 32).map((icon) => ({
194
+ id: icon.name,
195
+ prependIcon: icon.name
196
+ })));
197
+ }
198
+ }
199
+ else {
200
+ const matchingIcons = Icons.filter((icon) => {
201
+ return icon.fullText.toLowerCase().includes(innerValue.value.toLowerCase());
202
+ }).sort((a, b) => sortByLevenshteinDistance(a.name, b.name, innerValue.value));
203
+ innerIcons.push(...matchingIcons.slice(0, Math.min(32, matchingIcons.length)).map((icon) => ({
204
+ id: icon.name,
205
+ prependIcon: icon.name
206
+ })));
207
+ }
208
+ if (props.modelValue) {
209
+ const selectedIcon = innerIcons.find((icon) => icon.id === props.modelValue);
210
+ if (selectedIcon) {
211
+ innerIcons.splice(innerIcons.indexOf(selectedIcon), 1);
212
+ innerIcons.unshift(selectedIcon);
213
+ }
214
+ else {
215
+ innerIcons.unshift({
216
+ id: props.modelValue,
217
+ prependIcon: props.modelValue
218
+ });
219
+ }
220
+ }
221
+ return innerIcons;
222
+ });
223
+
224
+ return {
225
+ innerValue,
226
+ messages,
227
+ style,
228
+ icons
229
+ };
230
+ }
231
+ })
232
+ </script>
@@ -0,0 +1,142 @@
1
+ <template>
2
+ <v-img
3
+ class="fs-image"
4
+ :src="source"
5
+ :cover="$props.cover"
6
+ :width="computedWidth"
7
+ :height="computedHeight"
8
+ v-bind="$attrs"
9
+ >
10
+ <template #placeholder>
11
+ <v-skeleton-loader
12
+ type="image"
13
+ />
14
+ </template>
15
+ </v-img>
16
+ </template>
17
+
18
+ <script lang="ts">
19
+ import { computed, defineComponent, onMounted } from "vue";
20
+
21
+ import { useImageRaw, useImageBlurHash } from "@dative-gpi/foundation-shared-services";
22
+
23
+ export default defineComponent({
24
+ name: "FSImage",
25
+ props: {
26
+ imageId: {
27
+ type: String,
28
+ required: true
29
+ },
30
+ cover: {
31
+ type: Boolean,
32
+ required: false,
33
+ default: true
34
+ },
35
+ width: {
36
+ type: Number,
37
+ required: false,
38
+ default: null
39
+ },
40
+ height: {
41
+ type: Number,
42
+ required: false,
43
+ default: null
44
+ },
45
+ aspectRatio: {
46
+ type: String,
47
+ required: false,
48
+ default: null
49
+ }
50
+ },
51
+ setup(props) {
52
+ const { fetching: fetchingRaw, fetch: fetchRaw, fetched: image } = useImageRaw();
53
+ const { fetching: fetchingBlurHash, fetch: fetchBlurHash, fetched: blurHash } = useImageBlurHash();
54
+
55
+ const computedWidth = computed((): number => {
56
+ if (props.width) {
57
+ return props.width;
58
+ }
59
+ if (props.height) {
60
+ if (props.aspectRatio) {
61
+ const split = props.aspectRatio.split('/');
62
+ if (split.length === 2 && !isNaN(parseFloat(split[0])) && !isNaN(parseFloat(split[1]))) {
63
+ return props.height * (parseFloat(split[0]) / parseFloat(split[1]));
64
+ }
65
+ }
66
+ return props.height;
67
+ }
68
+ return 0;
69
+ });
70
+
71
+ const computedHeight = computed((): number => {
72
+ if (props.height) {
73
+ return props.height;
74
+ }
75
+ if (props.width) {
76
+ if (props.aspectRatio) {
77
+ const split = props.aspectRatio.split('/');
78
+ if (split.length === 2 && !isNaN(parseFloat(split[0])) && !isNaN(parseFloat(split[1]))) {
79
+ return props.width * (parseFloat(split[1]) / parseFloat(split[0]));
80
+ }
81
+ }
82
+ return props.width;
83
+ }
84
+ return 0;
85
+ });
86
+
87
+ const source = computed((): string | null => {
88
+ if (fetchingRaw.value) {
89
+ return null;
90
+ }
91
+ if (image.value) {
92
+ return image.value;
93
+ }
94
+ if (fetchingBlurHash.value) {
95
+ return null;
96
+ }
97
+ if (blurHash.value) {
98
+ return blurHash.value.blurHash;
99
+ }
100
+ return null;
101
+ });
102
+
103
+ // const pixels = () => {
104
+ // if (this.value && this.isValid)
105
+ // return decode(this.value, this.width, this.height, this.punch);
106
+ // return [];
107
+ // }
108
+
109
+ // const isValid = () => {
110
+ // return this.value && isBlurhashValid(this.value).result;
111
+ // }
112
+
113
+ // const reset = () => {
114
+ // const ctx = (this.$el as HTMLCanvasElement).getContext("2d");
115
+ // this.$nextTick(() => {
116
+ // if (this.pixels.length) {
117
+ // const imageData = ctx!.createImageData(this.width, this.height);
118
+ // imageData.data.set(this.pixels);
119
+ // ctx!.putImageData(imageData, 0, 0);
120
+ // }
121
+ // });
122
+ // }
123
+
124
+ onMounted(() => {
125
+ fetch();
126
+ });
127
+
128
+ const fetch = async () => {
129
+ await fetchRaw(props.imageId);
130
+ if (!image.value) {
131
+ await fetchBlurHash(props.imageId);
132
+ }
133
+ }
134
+
135
+ return {
136
+ source,
137
+ computedWidth,
138
+ computedHeight
139
+ };
140
+ }
141
+ });
142
+ </script>