@dative-gpi/foundation-shared-components 0.0.44 → 0.0.46
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.
- package/components/FSCheckbox.vue +62 -25
- package/components/FSFadeOut.vue +47 -13
- package/components/FSImage.vue +49 -33
- package/components/FSRadio.vue +63 -21
- package/components/FSSwitch.vue +31 -3
- package/components/fields/FSRichTextField.vue +8 -3
- package/components/lists/FSGrid.vue +131 -0
- package/models/grids.ts +12 -0
- package/models/index.ts +1 -0
- package/models/rules.ts +5 -1
- package/package.json +4 -4
- package/styles/components/fs_fade_out.scss +2 -6
- package/styles/components/fs_grid.scss +4 -0
- package/styles/components/fs_image.scss +0 -4
- package/styles/components/fs_switch.scss +1 -0
- package/styles/components/index.scss +1 -0
- package/styles/globals/overrides.scss +25 -3
|
@@ -2,30 +2,43 @@
|
|
|
2
2
|
<FSCol
|
|
3
3
|
width="hug"
|
|
4
4
|
>
|
|
5
|
-
<
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
<v-checkbox
|
|
6
|
+
hide-details
|
|
7
|
+
:ripple="false"
|
|
8
|
+
:rules="$props.rules"
|
|
9
|
+
:validateOn="validateOn"
|
|
10
|
+
:modelValue="$props.modelValue"
|
|
11
|
+
@click.prevent
|
|
12
|
+
@blur="blurred = true"
|
|
13
|
+
v-bind="$attrs"
|
|
8
14
|
>
|
|
9
|
-
<
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
@click.stop="onToggle"
|
|
14
|
-
>
|
|
15
|
-
{{ icon }}
|
|
16
|
-
</FSIcon>
|
|
17
|
-
<slot>
|
|
18
|
-
<FSSpan
|
|
19
|
-
v-if="$props.label"
|
|
20
|
-
class="fs-checkbox-label"
|
|
15
|
+
<template #input>
|
|
16
|
+
<FSRow
|
|
17
|
+
align="center-left"
|
|
18
|
+
width="hug"
|
|
21
19
|
:style="style"
|
|
22
|
-
:font="font"
|
|
23
20
|
@click.stop="onToggle"
|
|
24
21
|
>
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
<FSIcon
|
|
23
|
+
class="fs-checkbox"
|
|
24
|
+
size="l"
|
|
25
|
+
:style="style"
|
|
26
|
+
>
|
|
27
|
+
{{ icon }}
|
|
28
|
+
</FSIcon>
|
|
29
|
+
<slot>
|
|
30
|
+
<FSSpan
|
|
31
|
+
v-if="$props.label"
|
|
32
|
+
class="fs-checkbox-label"
|
|
33
|
+
:style="style"
|
|
34
|
+
:font="font"
|
|
35
|
+
>
|
|
36
|
+
{{ $props.label }}
|
|
37
|
+
</FSSpan>
|
|
38
|
+
</slot>
|
|
39
|
+
</FSRow>
|
|
40
|
+
</template>
|
|
41
|
+
</v-checkbox>
|
|
29
42
|
<slot name="description">
|
|
30
43
|
<FSSpan
|
|
31
44
|
v-if="$props.description"
|
|
@@ -42,8 +55,8 @@
|
|
|
42
55
|
<script lang="ts">
|
|
43
56
|
import { computed, defineComponent, PropType } from "vue";
|
|
44
57
|
|
|
58
|
+
import { useColors, useRules } from "@dative-gpi/foundation-shared-components/composables";
|
|
45
59
|
import { ColorBase, ColorEnum } from "@dative-gpi/foundation-shared-components/models";
|
|
46
|
-
import { useColors } from "@dative-gpi/foundation-shared-components/composables";
|
|
47
60
|
|
|
48
61
|
import FSIcon from "./FSIcon.vue";
|
|
49
62
|
import FSSpan from "./FSSpan.vue";
|
|
@@ -74,15 +87,25 @@ export default defineComponent({
|
|
|
74
87
|
required: false,
|
|
75
88
|
default: false
|
|
76
89
|
},
|
|
90
|
+
indeterminate: {
|
|
91
|
+
type: Boolean,
|
|
92
|
+
required: false,
|
|
93
|
+
default: false
|
|
94
|
+
},
|
|
77
95
|
color: {
|
|
78
96
|
type: String as PropType<ColorBase>,
|
|
79
97
|
required: false,
|
|
80
98
|
default: ColorEnum.Primary
|
|
81
99
|
},
|
|
82
|
-
|
|
83
|
-
type:
|
|
100
|
+
rules: {
|
|
101
|
+
type: Array as PropType<Function[]>,
|
|
84
102
|
required: false,
|
|
85
|
-
default:
|
|
103
|
+
default: () => []
|
|
104
|
+
},
|
|
105
|
+
messages: {
|
|
106
|
+
type: Array as PropType<string[]>,
|
|
107
|
+
required: false,
|
|
108
|
+
default: null
|
|
86
109
|
},
|
|
87
110
|
editable: {
|
|
88
111
|
type: Boolean,
|
|
@@ -92,9 +115,11 @@ export default defineComponent({
|
|
|
92
115
|
},
|
|
93
116
|
emits: ["update:modelValue"],
|
|
94
117
|
setup(props, { emit }) {
|
|
118
|
+
const { validateOn, blurred, getMessages } = useRules();
|
|
95
119
|
const { getColors } = useColors();
|
|
96
120
|
|
|
97
121
|
const colors = computed(() => getColors(props.color));
|
|
122
|
+
const errors = getColors(ColorEnum.Error);
|
|
98
123
|
const lights = getColors(ColorEnum.Light);
|
|
99
124
|
const darks = getColors(ColorEnum.Dark);
|
|
100
125
|
|
|
@@ -102,10 +127,17 @@ export default defineComponent({
|
|
|
102
127
|
if (!props.editable) {
|
|
103
128
|
return {
|
|
104
129
|
"--fs-checkbox-cursor" : "default",
|
|
105
|
-
"--fs-checkbox-checkbox-color": lights.
|
|
130
|
+
"--fs-checkbox-checkbox-color": (props.modelValue || props.indeterminate) ? colors.value.light : lights.base,
|
|
106
131
|
"--fs-checkbox-color" : lights.dark
|
|
107
132
|
};
|
|
108
133
|
}
|
|
134
|
+
if (messages.value.length) {
|
|
135
|
+
return {
|
|
136
|
+
"--fs-checkbox-cursor" : "pointer",
|
|
137
|
+
"--fs-checkbox-checkbox-color": errors.base,
|
|
138
|
+
"--fs-checkbox-color" : darks.base
|
|
139
|
+
}
|
|
140
|
+
}
|
|
109
141
|
return {
|
|
110
142
|
"--fs-checkbox-cursor" : "pointer",
|
|
111
143
|
"--fs-checkbox-checkbox-color": (props.modelValue || props.indeterminate) ? colors.value.base : darks.base,
|
|
@@ -117,6 +149,8 @@ export default defineComponent({
|
|
|
117
149
|
|
|
118
150
|
const font = computed((): string => props.modelValue ? "text-button" : "text-body");
|
|
119
151
|
|
|
152
|
+
const messages = computed((): string[] => props.messages ?? getMessages(props.modelValue, props.rules));
|
|
153
|
+
|
|
120
154
|
const onToggle = (): void => {
|
|
121
155
|
if (!props.editable) {
|
|
122
156
|
return;
|
|
@@ -125,6 +159,9 @@ export default defineComponent({
|
|
|
125
159
|
};
|
|
126
160
|
|
|
127
161
|
return {
|
|
162
|
+
validateOn,
|
|
163
|
+
messages,
|
|
164
|
+
blurred,
|
|
128
165
|
style,
|
|
129
166
|
icon,
|
|
130
167
|
font,
|
package/components/FSFadeOut.vue
CHANGED
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|
</template>
|
|
13
13
|
|
|
14
14
|
<script lang="ts">
|
|
15
|
-
import { computed, defineComponent, onMounted, PropType, ref } from "vue";
|
|
15
|
+
import { computed, defineComponent, onMounted, PropType, ref, watch } from "vue";
|
|
16
16
|
|
|
17
|
-
import { useColors } from "@dative-gpi/foundation-shared-components/composables";
|
|
17
|
+
import { useBreakpoints, useColors, useDebounce } from "@dative-gpi/foundation-shared-components/composables";
|
|
18
18
|
import { ColorEnum } from "@dative-gpi/foundation-shared-components/models";
|
|
19
19
|
import { sizeToVar } from "@dative-gpi/foundation-shared-components/utils";
|
|
20
20
|
|
|
@@ -42,24 +42,26 @@ export default defineComponent({
|
|
|
42
42
|
}
|
|
43
43
|
},
|
|
44
44
|
setup(props) {
|
|
45
|
+
const { windowWidth } = useBreakpoints();
|
|
46
|
+
const { debounce } = useDebounce();
|
|
45
47
|
const { getColors } = useColors();
|
|
46
48
|
|
|
47
49
|
const backgrounds = getColors(ColorEnum.Background);
|
|
48
50
|
|
|
49
51
|
const fadeOutRef = ref(null);
|
|
50
|
-
const topMaskHeight = ref("0px");
|
|
51
52
|
const bottomMaskHeight = ref("0px");
|
|
53
|
+
const topMaskHeight = ref("0px");
|
|
52
54
|
|
|
53
55
|
const style = computed((): { [code: string]: string } & Partial<CSSStyleDeclaration> => {
|
|
54
56
|
return {
|
|
55
|
-
"--fs-fade-out-height"
|
|
56
|
-
"--fs-fade-out-width"
|
|
57
|
-
"--fs-fade-out-padding"
|
|
58
|
-
"--fs-fade-out-mask-color"
|
|
59
|
-
"--fs-fade-out-top-mask-height"
|
|
60
|
-
"--fs-fade-out-top-mask-top"
|
|
61
|
-
"--fs-fade-out-bottom-mask-height": bottomMaskHeight.value,
|
|
62
|
-
"--fs-fade-out-bottom-mask-bottom": bottomPadding.value
|
|
57
|
+
"--fs-fade-out-height" : sizeToVar(props.height),
|
|
58
|
+
"--fs-fade-out-width" : sizeToVar(props.width),
|
|
59
|
+
"--fs-fade-out-padding" : sizeToVar(props.padding),
|
|
60
|
+
"--fs-fade-out-mask-color" : backgrounds.base,
|
|
61
|
+
"--fs-fade-out-top-mask-height" : topMaskHeight.value,
|
|
62
|
+
"--fs-fade-out-top-mask-top" : topPadding.value,
|
|
63
|
+
"--fs-fade-out-bottom-mask-height" : bottomMaskHeight.value,
|
|
64
|
+
"--fs-fade-out-bottom-mask-bottom" : bottomPadding.value
|
|
63
65
|
};
|
|
64
66
|
});
|
|
65
67
|
|
|
@@ -107,7 +109,16 @@ export default defineComponent({
|
|
|
107
109
|
}
|
|
108
110
|
});
|
|
109
111
|
|
|
110
|
-
const onScroll = ({ target }): void => {
|
|
112
|
+
const onScroll = ({ target }): void => debounce(() => {
|
|
113
|
+
const currentTopMaskHeight = target.children[0].clientHeight;
|
|
114
|
+
const currentBottomMaskHeight = target.children[target.children.length - 1].clientHeight;
|
|
115
|
+
const contentHeight = target.scrollHeight - currentTopMaskHeight - currentBottomMaskHeight;
|
|
116
|
+
|
|
117
|
+
if (target.clientHeight >= contentHeight) {
|
|
118
|
+
bottomMaskHeight.value = "0px";
|
|
119
|
+
topMaskHeight.value = "0px";
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
111
122
|
if (target.scrollHeight - target.scrollTop - target.clientHeight < 1) {
|
|
112
123
|
bottomMaskHeight.value = "0px";
|
|
113
124
|
}
|
|
@@ -120,7 +131,28 @@ export default defineComponent({
|
|
|
120
131
|
else {
|
|
121
132
|
topMaskHeight.value = sizeToVar(props.maskHeight);
|
|
122
133
|
}
|
|
123
|
-
}
|
|
134
|
+
}, 25);
|
|
135
|
+
|
|
136
|
+
const onResize = (): void => debounce(() => {
|
|
137
|
+
if (fadeOutRef.value) {
|
|
138
|
+
const currentTopMaskHeight = fadeOutRef.value.children[0].clientHeight;
|
|
139
|
+
const currentBottomMaskHeight = fadeOutRef.value.children[fadeOutRef.value.children.length - 1].clientHeight;
|
|
140
|
+
const contentHeight = fadeOutRef.value.scrollHeight - currentTopMaskHeight - currentBottomMaskHeight;
|
|
141
|
+
|
|
142
|
+
if (fadeOutRef.value.clientHeight < contentHeight) {
|
|
143
|
+
if (fadeOutRef.value.scrollHeight - fadeOutRef.value.scrollTop - fadeOutRef.value.clientHeight > 0) {
|
|
144
|
+
bottomMaskHeight.value = sizeToVar(props.maskHeight);
|
|
145
|
+
}
|
|
146
|
+
if (fadeOutRef.value.scrollTop > 0) {
|
|
147
|
+
topMaskHeight.value = sizeToVar(props.maskHeight);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
bottomMaskHeight.value = "0px";
|
|
152
|
+
topMaskHeight.value = "0px";
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}, 200);
|
|
124
156
|
|
|
125
157
|
onMounted((): void => {
|
|
126
158
|
if (fadeOutRef.value) {
|
|
@@ -130,6 +162,8 @@ export default defineComponent({
|
|
|
130
162
|
}
|
|
131
163
|
});
|
|
132
164
|
|
|
165
|
+
watch(() => windowWidth.value, onResize);
|
|
166
|
+
|
|
133
167
|
return {
|
|
134
168
|
fadeOutRef,
|
|
135
169
|
style,
|
package/components/FSImage.vue
CHANGED
|
@@ -1,28 +1,44 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<v-img
|
|
3
3
|
class="fs-image"
|
|
4
|
+
ref="imageRef"
|
|
4
5
|
:height="computedHeight"
|
|
5
6
|
:width="computedWidth"
|
|
6
7
|
:cover="$props.cover"
|
|
7
8
|
:style="style"
|
|
8
9
|
:src="source"
|
|
10
|
+
@error="onError"
|
|
9
11
|
v-bind="$attrs"
|
|
10
12
|
>
|
|
11
13
|
<template #placeholder>
|
|
12
14
|
<FSLoader
|
|
13
|
-
class="fs-load
|
|
15
|
+
class="fs-image-load"
|
|
14
16
|
height="100%"
|
|
15
17
|
width="100%"
|
|
16
18
|
:borderRadius="$props.borderRadius"
|
|
17
19
|
/>
|
|
18
20
|
</template>
|
|
21
|
+
<template #error>
|
|
22
|
+
<FSLoader
|
|
23
|
+
v-if="!blurHash"
|
|
24
|
+
class="fs-image-load"
|
|
25
|
+
height="100%"
|
|
26
|
+
width="100%"
|
|
27
|
+
:borderRadius="$props.borderRadius"
|
|
28
|
+
/>
|
|
29
|
+
<canvas
|
|
30
|
+
ref="canvasRef"
|
|
31
|
+
/>
|
|
32
|
+
</template>
|
|
19
33
|
</v-img>
|
|
20
34
|
</template>
|
|
21
35
|
|
|
22
36
|
<script lang="ts">
|
|
23
|
-
import { computed, defineComponent,
|
|
37
|
+
import { computed, defineComponent, ref, watch } from "vue";
|
|
38
|
+
import { decode, isBlurhashValid } from "blurhash";
|
|
24
39
|
|
|
25
|
-
import {
|
|
40
|
+
import { useImageBlurHash } from "@dative-gpi/foundation-shared-services/composables";
|
|
41
|
+
import { IMAGE_RAW_URL } from "@dative-gpi/foundation-shared-services/config/urls";
|
|
26
42
|
import { sizeToVar } from "@dative-gpi/foundation-shared-components/utils";
|
|
27
43
|
|
|
28
44
|
import FSLoader from "./FSLoader.vue";
|
|
@@ -65,12 +81,15 @@ export default defineComponent({
|
|
|
65
81
|
}
|
|
66
82
|
},
|
|
67
83
|
setup(props) {
|
|
68
|
-
const {
|
|
69
|
-
|
|
84
|
+
const { get: fetchBlurHash, entity: blurHash } = useImageBlurHash();
|
|
85
|
+
|
|
86
|
+
const imageRef = ref(null);
|
|
87
|
+
const canvasRef = ref(null);
|
|
70
88
|
|
|
71
89
|
const style = computed((): { [code: string]: string } & Partial<CSSStyleDeclaration> => {
|
|
72
90
|
return {
|
|
73
|
-
"--fs-image-border-radius": sizeToVar(props.borderRadius)
|
|
91
|
+
"--fs-image-border-radius" : sizeToVar(props.borderRadius),
|
|
92
|
+
"--fs-image-blurhash-opacity": blurHash.value ? "1" : "0"
|
|
74
93
|
}
|
|
75
94
|
});
|
|
76
95
|
|
|
@@ -113,44 +132,41 @@ export default defineComponent({
|
|
|
113
132
|
});
|
|
114
133
|
|
|
115
134
|
const source = computed((): string | null => {
|
|
116
|
-
if (
|
|
117
|
-
return
|
|
118
|
-
}
|
|
119
|
-
if (image.value) {
|
|
120
|
-
return image.value;
|
|
121
|
-
}
|
|
122
|
-
if (fetchingBlurHash.value) {
|
|
123
|
-
return null;
|
|
124
|
-
}
|
|
125
|
-
if (blurHash.value) {
|
|
126
|
-
return blurHash.value.blurHash;
|
|
135
|
+
if (props.imageId) {
|
|
136
|
+
return IMAGE_RAW_URL(props.imageId);
|
|
127
137
|
}
|
|
128
138
|
return null;
|
|
129
139
|
});
|
|
130
140
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
watch(() => props.imageId, () => {
|
|
136
|
-
fetch();
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
const fetch = async () => {
|
|
140
|
-
if (!props.imageId) {
|
|
141
|
-
return;
|
|
141
|
+
const onError = (): void => {
|
|
142
|
+
if (props.imageId) {
|
|
143
|
+
fetchBlurHash(props.imageId);
|
|
142
144
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
watch(() => blurHash.value, () => {
|
|
148
|
+
if (canvasRef.value && imageRef.value) {
|
|
149
|
+
if (blurHash.value && isBlurhashValid(blurHash.value.blurHash).result) {
|
|
150
|
+
const ctx = canvasRef.value.getContext("2d");
|
|
151
|
+
if (ctx) {
|
|
152
|
+
const pixels = decode(blurHash.value.blurHash, imageRef.value.$el.clientWidth, imageRef.value.$el.clientHeight);
|
|
153
|
+
const imageData = ctx.createImageData(imageRef.value.$el.clientWidth, imageRef.value.$el.clientHeight);
|
|
154
|
+
imageData.data.set(pixels);
|
|
155
|
+
ctx.putImageData(imageData, 0, 0);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
146
158
|
}
|
|
147
|
-
}
|
|
159
|
+
});
|
|
148
160
|
|
|
149
161
|
return {
|
|
150
162
|
computedHeight,
|
|
151
163
|
computedWidth,
|
|
164
|
+
canvasRef,
|
|
165
|
+
imageRef,
|
|
166
|
+
blurHash,
|
|
152
167
|
source,
|
|
153
|
-
style
|
|
168
|
+
style,
|
|
169
|
+
onError
|
|
154
170
|
};
|
|
155
171
|
}
|
|
156
172
|
});
|
package/components/FSRadio.vue
CHANGED
|
@@ -1,26 +1,44 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<FSCol
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
2
|
+
<FSCol
|
|
3
|
+
width="hug"
|
|
4
|
+
>
|
|
5
|
+
<v-radio
|
|
6
|
+
hide-details
|
|
7
|
+
:ripple="false"
|
|
8
|
+
:rules="$props.rules"
|
|
9
|
+
:validateOn="validateOn"
|
|
10
|
+
:modelValue="$props.selected"
|
|
11
|
+
@click.prevent
|
|
12
|
+
@blur="blurred = true"
|
|
13
|
+
v-bind="$attrs"
|
|
14
|
+
>
|
|
15
|
+
<template #input>
|
|
16
|
+
<FSRow
|
|
17
|
+
align="center-left"
|
|
18
|
+
width="hug"
|
|
16
19
|
:style="style"
|
|
17
|
-
:font="font"
|
|
18
20
|
@click.stop="onToggle"
|
|
19
21
|
>
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
<FSIcon
|
|
23
|
+
class="fs-radio"
|
|
24
|
+
size="l"
|
|
25
|
+
:style="style"
|
|
26
|
+
>
|
|
27
|
+
{{ icon }}
|
|
28
|
+
</FSIcon>
|
|
29
|
+
<slot>
|
|
30
|
+
<FSSpan
|
|
31
|
+
v-if="$props.label"
|
|
32
|
+
class="fs-radio-label"
|
|
33
|
+
:style="style"
|
|
34
|
+
:font="font"
|
|
35
|
+
>
|
|
36
|
+
{{ $props.label }}
|
|
37
|
+
</FSSpan>
|
|
38
|
+
</slot>
|
|
39
|
+
</FSRow>
|
|
40
|
+
</template>
|
|
41
|
+
</v-radio>
|
|
24
42
|
<slot name="description">
|
|
25
43
|
<FSSpan
|
|
26
44
|
v-if="$props.description"
|
|
@@ -37,8 +55,8 @@
|
|
|
37
55
|
<script lang="ts">
|
|
38
56
|
import { computed, defineComponent, PropType } from "vue";
|
|
39
57
|
|
|
58
|
+
import { useColors, useRules } from "@dative-gpi/foundation-shared-components/composables";
|
|
40
59
|
import { ColorBase, ColorEnum } from "@dative-gpi/foundation-shared-components/models";
|
|
41
|
-
import { useColors } from "@dative-gpi/foundation-shared-components/composables";
|
|
42
60
|
|
|
43
61
|
import FSIcon from "./FSIcon.vue";
|
|
44
62
|
import FSSpan from "./FSSpan.vue";
|
|
@@ -78,6 +96,16 @@ export default defineComponent({
|
|
|
78
96
|
required: false,
|
|
79
97
|
default: ColorEnum.Primary
|
|
80
98
|
},
|
|
99
|
+
rules: {
|
|
100
|
+
type: Array as PropType<Function[]>,
|
|
101
|
+
required: false,
|
|
102
|
+
default: () => []
|
|
103
|
+
},
|
|
104
|
+
messages: {
|
|
105
|
+
type: Array as PropType<string[]>,
|
|
106
|
+
required: false,
|
|
107
|
+
default: null
|
|
108
|
+
},
|
|
81
109
|
editable: {
|
|
82
110
|
type: Boolean,
|
|
83
111
|
required: false,
|
|
@@ -86,9 +114,11 @@ export default defineComponent({
|
|
|
86
114
|
},
|
|
87
115
|
emits: ["update:modelValue"],
|
|
88
116
|
setup(props, { emit }) {
|
|
117
|
+
const { validateOn, blurred, getMessages } = useRules();
|
|
89
118
|
const { getColors } = useColors();
|
|
90
119
|
|
|
91
120
|
const colors = computed(() => getColors(props.color));
|
|
121
|
+
const errors = getColors(ColorEnum.Error);
|
|
92
122
|
const lights = getColors(ColorEnum.Light);
|
|
93
123
|
const darks = getColors(ColorEnum.Dark);
|
|
94
124
|
|
|
@@ -96,10 +126,17 @@ export default defineComponent({
|
|
|
96
126
|
if (!props.editable) {
|
|
97
127
|
return {
|
|
98
128
|
"--fs-radio-cursor" : "default",
|
|
99
|
-
"--fs-radio-radio-color": lights.
|
|
129
|
+
"--fs-radio-radio-color": props.selected ? colors.value.light : lights.base,
|
|
100
130
|
"--fs-radio-color" : lights.dark
|
|
101
131
|
};
|
|
102
132
|
}
|
|
133
|
+
if (messages.value.length) {
|
|
134
|
+
return {
|
|
135
|
+
"--fs-radio-cursor" : "pointer",
|
|
136
|
+
"--fs-radio-radio-color": errors.base,
|
|
137
|
+
"--fs-radio-color" : darks.base
|
|
138
|
+
}
|
|
139
|
+
}
|
|
103
140
|
return {
|
|
104
141
|
"--fs-radio-cursor" : props.selected ? "default" : "pointer",
|
|
105
142
|
"--fs-radio-radio-color": props.selected ? colors.value.base : darks.base,
|
|
@@ -111,6 +148,8 @@ export default defineComponent({
|
|
|
111
148
|
|
|
112
149
|
const font = computed((): string => props.selected ? "text-button" : "text-body");
|
|
113
150
|
|
|
151
|
+
const messages = computed((): string[] => props.messages ?? getMessages(props.modelValue, props.rules));
|
|
152
|
+
|
|
114
153
|
const onToggle = (): void => {
|
|
115
154
|
if (!props.editable) {
|
|
116
155
|
return;
|
|
@@ -121,6 +160,9 @@ export default defineComponent({
|
|
|
121
160
|
};
|
|
122
161
|
|
|
123
162
|
return {
|
|
163
|
+
validateOn,
|
|
164
|
+
messages,
|
|
165
|
+
blurred,
|
|
124
166
|
style,
|
|
125
167
|
icon,
|
|
126
168
|
font,
|
package/components/FSSwitch.vue
CHANGED
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
inset
|
|
13
13
|
:style="style"
|
|
14
14
|
:ripple="false"
|
|
15
|
+
:rules="$props.rules"
|
|
16
|
+
:validateOn="validateOn"
|
|
15
17
|
:modelValue="$props.modelValue"
|
|
16
18
|
@update:modelValue="onToggle"
|
|
17
19
|
v-bind="$attrs"
|
|
@@ -44,8 +46,8 @@
|
|
|
44
46
|
<script lang="ts">
|
|
45
47
|
import { computed, defineComponent, PropType } from "vue";
|
|
46
48
|
|
|
49
|
+
import { useColors, useRules } from "@dative-gpi/foundation-shared-components/composables";
|
|
47
50
|
import { ColorBase, ColorEnum } from "@dative-gpi/foundation-shared-components/models";
|
|
48
|
-
import { useColors } from "@dative-gpi/foundation-shared-components/composables";
|
|
49
51
|
|
|
50
52
|
import FSSpan from "./FSSpan.vue";
|
|
51
53
|
import FSCol from "./FSCol.vue";
|
|
@@ -79,6 +81,16 @@ export default defineComponent({
|
|
|
79
81
|
required: false,
|
|
80
82
|
default: ColorEnum.Primary
|
|
81
83
|
},
|
|
84
|
+
rules: {
|
|
85
|
+
type: Array as PropType<Function[]>,
|
|
86
|
+
required: false,
|
|
87
|
+
default: () => []
|
|
88
|
+
},
|
|
89
|
+
messages: {
|
|
90
|
+
type: Array as PropType<string[]>,
|
|
91
|
+
required: false,
|
|
92
|
+
default: null
|
|
93
|
+
},
|
|
82
94
|
editable: {
|
|
83
95
|
type: Boolean,
|
|
84
96
|
required: false,
|
|
@@ -87,10 +99,12 @@ export default defineComponent({
|
|
|
87
99
|
},
|
|
88
100
|
emits: ["update:modelValue"],
|
|
89
101
|
setup(props, { emit }) {
|
|
102
|
+
const { validateOn, blurred, getMessages } = useRules();
|
|
90
103
|
const { getColors } = useColors();
|
|
91
104
|
|
|
92
105
|
const colors = computed(() => getColors(props.color));
|
|
93
106
|
const backgrounds = getColors(ColorEnum.Background);
|
|
107
|
+
const errors = getColors(ColorEnum.Error);
|
|
94
108
|
const lights = getColors(ColorEnum.Light);
|
|
95
109
|
const darks = getColors(ColorEnum.Dark);
|
|
96
110
|
|
|
@@ -99,15 +113,24 @@ export default defineComponent({
|
|
|
99
113
|
return {
|
|
100
114
|
"--fs-switch-translate-x": props.modelValue ? "8px" : "-8px",
|
|
101
115
|
"--fs-switch-cursor" : "default",
|
|
102
|
-
"--fs-switch-track-color": lights.
|
|
116
|
+
"--fs-switch-track-color": props.modelValue ? colors.value.light : lights.base,
|
|
103
117
|
"--fs-switch-thumb-color": backgrounds.base,
|
|
104
118
|
"--fs-switch-color" : lights.dark
|
|
105
119
|
};
|
|
106
120
|
}
|
|
121
|
+
if (messages.value.length) {
|
|
122
|
+
return {
|
|
123
|
+
"--fs-switch-translate-x": props.modelValue ? "8px" : "-8px",
|
|
124
|
+
"--fs-switch-cursor" : "pointer",
|
|
125
|
+
"--fs-switch-track-color": errors.base,
|
|
126
|
+
"--fs-switch-thumb-color": backgrounds.base,
|
|
127
|
+
"--fs-switch-color" : darks.base
|
|
128
|
+
};
|
|
129
|
+
}
|
|
107
130
|
return {
|
|
108
131
|
"--fs-switch-translate-x": props.modelValue ? "8px" : "-8px",
|
|
109
132
|
"--fs-switch-cursor" : "pointer",
|
|
110
|
-
"--fs-switch-track-color": props.modelValue ? colors.value.base :
|
|
133
|
+
"--fs-switch-track-color": props.modelValue ? colors.value.base : lights.dark,
|
|
111
134
|
"--fs-switch-thumb-color": backgrounds.base,
|
|
112
135
|
"--fs-switch-color" : darks.base
|
|
113
136
|
};
|
|
@@ -115,6 +138,8 @@ export default defineComponent({
|
|
|
115
138
|
|
|
116
139
|
const font = computed((): string => props.modelValue ? "text-button" : "text-body");
|
|
117
140
|
|
|
141
|
+
const messages = computed((): string[] => props.messages ?? getMessages(props.modelValue, props.rules));
|
|
142
|
+
|
|
118
143
|
const onToggle = (): void => {
|
|
119
144
|
if (!props.editable) {
|
|
120
145
|
return;
|
|
@@ -123,6 +148,9 @@ export default defineComponent({
|
|
|
123
148
|
}
|
|
124
149
|
|
|
125
150
|
return {
|
|
151
|
+
validateOn,
|
|
152
|
+
messages,
|
|
153
|
+
blurred,
|
|
126
154
|
style,
|
|
127
155
|
font,
|
|
128
156
|
onToggle
|
|
@@ -383,7 +383,12 @@ export default defineComponent({
|
|
|
383
383
|
editor.registerUpdateListener(({ editorState }) => {
|
|
384
384
|
editorState.read(() => {
|
|
385
385
|
updateToolbar();
|
|
386
|
-
|
|
386
|
+
if (JSON.stringify(editorState.toJSON()) !== emptyState) {
|
|
387
|
+
emit("update:modelValue", JSON.stringify(editorState.toJSON()));
|
|
388
|
+
}
|
|
389
|
+
else {
|
|
390
|
+
emit("update:modelValue", null);
|
|
391
|
+
}
|
|
387
392
|
});
|
|
388
393
|
});
|
|
389
394
|
|
|
@@ -541,13 +546,13 @@ export default defineComponent({
|
|
|
541
546
|
}
|
|
542
547
|
|
|
543
548
|
watch(() => props.modelValue, () => {
|
|
544
|
-
if (
|
|
549
|
+
if (JSON.stringify(editor.getEditorState().toJSON()) != props.modelValue) {
|
|
545
550
|
if (props.modelValue != null) {
|
|
546
551
|
editor.update(() => {
|
|
547
552
|
editor.setEditorState(editor.parseEditorState(props.modelValue));
|
|
548
553
|
});
|
|
549
554
|
}
|
|
550
|
-
else {
|
|
555
|
+
else if (JSON.stringify(editor.getEditorState().toJSON()) !== emptyState) {
|
|
551
556
|
editor.update(() => {
|
|
552
557
|
editor.setEditorState(editor.parseEditorState(emptyState));
|
|
553
558
|
});
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<FSRow
|
|
3
|
+
gap="32px"
|
|
4
|
+
>
|
|
5
|
+
<FSRow
|
|
6
|
+
v-for="(gridItem, index) in gridItems"
|
|
7
|
+
:width="width"
|
|
8
|
+
:style="style"
|
|
9
|
+
:key="index"
|
|
10
|
+
>
|
|
11
|
+
<FSCol
|
|
12
|
+
gap="16px"
|
|
13
|
+
>
|
|
14
|
+
<FSText
|
|
15
|
+
font="text-h3"
|
|
16
|
+
>
|
|
17
|
+
{{ gridItem.categoryLabel }}
|
|
18
|
+
</FSText>
|
|
19
|
+
<FSCol
|
|
20
|
+
gap="0"
|
|
21
|
+
>
|
|
22
|
+
<FSRow
|
|
23
|
+
v-for="(item, index) in gridItem.items"
|
|
24
|
+
padding="0 8px"
|
|
25
|
+
align="center-center"
|
|
26
|
+
class="fs-grid-item"
|
|
27
|
+
height="hug"
|
|
28
|
+
:key="index"
|
|
29
|
+
>
|
|
30
|
+
<FSCol
|
|
31
|
+
gap="2px"
|
|
32
|
+
>
|
|
33
|
+
<slot :name="`item-header.${gridItem.categoryCode}-${item.code}`" v-bind="{ item }">
|
|
34
|
+
<FSText
|
|
35
|
+
:font="item.hideDefault ? 'text-body' : 'text-overline'"
|
|
36
|
+
>
|
|
37
|
+
{{ item.label }}
|
|
38
|
+
</FSText>
|
|
39
|
+
</slot>
|
|
40
|
+
<FSRow
|
|
41
|
+
v-if="!item.hideDefault"
|
|
42
|
+
>
|
|
43
|
+
<slot
|
|
44
|
+
:name="`item-value-left.${gridItem.categoryCode}-${item.code}`"
|
|
45
|
+
v-bind="{ item }"
|
|
46
|
+
>
|
|
47
|
+
<FSText>
|
|
48
|
+
{{ item.value }}
|
|
49
|
+
</FSText>
|
|
50
|
+
</slot>
|
|
51
|
+
</FSRow>
|
|
52
|
+
</FSCol>
|
|
53
|
+
<v-spacer />
|
|
54
|
+
<FSRow
|
|
55
|
+
v-if="useSlot(`item-value-right.${gridItem.categoryCode}-${item.code}`)"
|
|
56
|
+
align="center-right"
|
|
57
|
+
>
|
|
58
|
+
<slot
|
|
59
|
+
:name="`item-value-right.${gridItem.categoryCode}-${item.code}`"
|
|
60
|
+
v-bind="{ item }"
|
|
61
|
+
>
|
|
62
|
+
<FSText
|
|
63
|
+
font="text-body"
|
|
64
|
+
>
|
|
65
|
+
{{ item.value }}
|
|
66
|
+
</FSText>
|
|
67
|
+
</slot>
|
|
68
|
+
</FSRow>
|
|
69
|
+
</FSRow>
|
|
70
|
+
</FSCol>
|
|
71
|
+
</FSCol>
|
|
72
|
+
</FSRow>
|
|
73
|
+
</FSRow>
|
|
74
|
+
</template>
|
|
75
|
+
|
|
76
|
+
<script lang="ts">
|
|
77
|
+
import { computed, defineComponent, PropType } from "vue";
|
|
78
|
+
|
|
79
|
+
import { useBreakpoints, useColors, useSlots } from "@dative-gpi/foundation-shared-components/composables";
|
|
80
|
+
import { ColorEnum, GridItem } from "@dative-gpi/foundation-shared-components/models";
|
|
81
|
+
|
|
82
|
+
import FSDivider from "../FSDivider.vue";
|
|
83
|
+
import FSText from "../FSText.vue";
|
|
84
|
+
|
|
85
|
+
export default defineComponent({
|
|
86
|
+
name: "FSGrid",
|
|
87
|
+
components: {
|
|
88
|
+
FSDivider,
|
|
89
|
+
FSText
|
|
90
|
+
},
|
|
91
|
+
props: {
|
|
92
|
+
gridItems: {
|
|
93
|
+
type: Array as PropType<GridItem[]>,
|
|
94
|
+
default: [],
|
|
95
|
+
required: false
|
|
96
|
+
},
|
|
97
|
+
cols: {
|
|
98
|
+
type: Number as PropType<1 | 2>,
|
|
99
|
+
required: false,
|
|
100
|
+
default: 1
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
setup(props) {
|
|
104
|
+
const { isExtraSmall } = useBreakpoints();
|
|
105
|
+
const { getColors } = useColors();
|
|
106
|
+
const { slots } = useSlots();
|
|
107
|
+
|
|
108
|
+
const lights = getColors(ColorEnum.Light);
|
|
109
|
+
|
|
110
|
+
const style = computed((): {[code: string]: string} & Partial<CSSStyleDeclaration> => {
|
|
111
|
+
return {
|
|
112
|
+
"--fs-grid-border-color": lights.dark
|
|
113
|
+
};
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
const width = computed(() => {
|
|
117
|
+
return props.cols == 2 && !isExtraSmall.value ? "calc(50% - 16px)" : "100%";
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const useSlot = (name: string): boolean => {
|
|
121
|
+
return !!slots[name];
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
width,
|
|
126
|
+
style,
|
|
127
|
+
useSlot
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
})
|
|
131
|
+
</script>
|
package/models/grids.ts
ADDED
package/models/index.ts
CHANGED
package/models/rules.ts
CHANGED
|
@@ -58,4 +58,8 @@ export const TimeRules = {
|
|
|
58
58
|
required: (message: string) => (value: number) => !!value || (message ?? $tr("ui.rules.required", "Required")),
|
|
59
59
|
min: (min: number, message: string) => (value: number) => value >= min || (message ?? $tr("ui.rules.time-min", "Must be more than {0}", getTimeBestString(min))),
|
|
60
60
|
max: (max: number, message: string) => (value: number) => value <= max || (message ?? $tr("ui.rules.time-max", "Must be less than {0}", getTimeBestString(max)))
|
|
61
|
-
};
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export const ToggleRules = {
|
|
64
|
+
required: (message: string) => (value: boolean) => value || (message ?? $tr("ui.rules.required", "Required"))
|
|
65
|
+
}
|
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.
|
|
4
|
+
"version": "0.0.46",
|
|
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.
|
|
14
|
-
"@dative-gpi/foundation-shared-services": "0.0.
|
|
13
|
+
"@dative-gpi/foundation-shared-domain": "0.0.46",
|
|
14
|
+
"@dative-gpi/foundation-shared-services": "0.0.46",
|
|
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": "
|
|
35
|
+
"gitHead": "a1963363ed2f74aa24319fae34baeaa4d54e5e97"
|
|
36
36
|
}
|
|
@@ -12,25 +12,21 @@
|
|
|
12
12
|
.fs-fade-out-top {
|
|
13
13
|
pointer-events: none;
|
|
14
14
|
position: sticky;
|
|
15
|
-
display: flex;
|
|
16
15
|
z-index: 10;
|
|
17
|
-
height: var(--fs-fade-out-top-mask-height);
|
|
18
16
|
min-height: var(--fs-fade-out-top-mask-height);
|
|
19
17
|
width: 100%;
|
|
20
18
|
top: var(--fs-fade-out-top-mask-top);
|
|
21
|
-
transition: all 0.
|
|
19
|
+
transition: all 0.14s cubic-bezier(0.4, 0, 0.2, 1);
|
|
22
20
|
background: linear-gradient(to top, transparent 0, var(--fs-fade-out-mask-color) var(--fs-fade-out-top-mask-height));
|
|
23
21
|
}
|
|
24
22
|
|
|
25
23
|
.fs-fade-out-bottom {
|
|
26
24
|
pointer-events: none;
|
|
27
25
|
position: sticky;
|
|
28
|
-
display: flex;
|
|
29
26
|
z-index: 10;
|
|
30
|
-
height: var(--fs-fade-out-bottom-mask-height);
|
|
31
27
|
min-height: var(--fs-fade-out-bottom-mask-height);
|
|
32
28
|
width: 100%;
|
|
33
29
|
bottom: var(--fs-fade-out-bottom-mask-bottom);
|
|
34
|
-
transition: all 0.
|
|
30
|
+
transition: all 0.14s cubic-bezier(0.4, 0, 0.2, 1);
|
|
35
31
|
background: linear-gradient(to bottom, transparent 0, var(--fs-fade-out-mask-color) var(--fs-fade-out-bottom-mask-height));
|
|
36
32
|
}
|
|
@@ -3,9 +3,27 @@
|
|
|
3
3
|
display: none;
|
|
4
4
|
}
|
|
5
5
|
|
|
6
|
-
// No default hover effect on switches and others
|
|
7
|
-
.v-selection-
|
|
8
|
-
|
|
6
|
+
// No default hover effect on switches and others, no limited opacity, no limited width...
|
|
7
|
+
.v-selection-control {
|
|
8
|
+
min-height: 0 !important;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.v-selection-control__wrapper {
|
|
12
|
+
height: auto !important;
|
|
13
|
+
width: auto !important;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.v-selection-control__input {
|
|
17
|
+
height: auto !important;
|
|
18
|
+
width: auto !important;
|
|
19
|
+
|
|
20
|
+
& > .v-icon {
|
|
21
|
+
opacity: 1 !important;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
&::before {
|
|
25
|
+
display: none;
|
|
26
|
+
}
|
|
9
27
|
}
|
|
10
28
|
|
|
11
29
|
// Applies to all inputs
|
|
@@ -64,6 +82,10 @@
|
|
|
64
82
|
}
|
|
65
83
|
}
|
|
66
84
|
|
|
85
|
+
// No input messages allowed
|
|
86
|
+
.v-input__details {
|
|
87
|
+
display: none !important;
|
|
88
|
+
}
|
|
67
89
|
|
|
68
90
|
// No up / down buttons in input field of type number
|
|
69
91
|
input[type=number] {
|