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

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 (37) hide show
  1. package/components/FSButton.vue +170 -164
  2. package/components/FSCalendar.vue +171 -0
  3. package/components/FSCalendarTwin.vue +394 -0
  4. package/components/FSCard.vue +63 -0
  5. package/components/FSCheckbox.vue +7 -8
  6. package/components/FSClock.vue +38 -0
  7. package/components/FSDatePicker.vue +226 -0
  8. package/components/FSIcon.vue +1 -1
  9. package/components/FSNumberField.vue +4 -4
  10. package/components/FSPasswordField.vue +14 -12
  11. package/components/FSRadio.vue +0 -1
  12. package/components/FSRadioGroup.vue +6 -6
  13. package/components/FSRichTextField.vue +558 -0
  14. package/components/FSSearchField.vue +103 -102
  15. package/components/FSSlider.vue +132 -0
  16. package/components/FSSwitch.vue +9 -9
  17. package/components/FSTagField.vue +186 -127
  18. package/components/FSTextArea.vue +207 -0
  19. package/components/FSTextField.vue +151 -146
  20. package/composables/index.ts +2 -1
  21. package/composables/useBreakpoints.ts +14 -0
  22. package/composables/useDates.ts +39 -0
  23. package/models/FSTextFields.ts +12 -6
  24. package/package.json +12 -4
  25. package/styles/components/fs_button.scss +2 -10
  26. package/styles/components/fs_calendar.scss +115 -0
  27. package/styles/components/fs_card.scss +7 -0
  28. package/styles/components/fs_date_picker.scss +0 -0
  29. package/styles/components/fs_icon.scss +3 -9
  30. package/styles/components/fs_rich_text_field.scss +67 -0
  31. package/styles/components/fs_slider.scss +20 -0
  32. package/styles/components/fs_tag_field.scss +9 -0
  33. package/styles/components/fs_text_area.scss +105 -0
  34. package/styles/components/index.scss +6 -0
  35. package/utils/FSRichTextField.ts +27 -0
  36. package/utils/index.ts +1 -0
  37. package/composables/useTouch.ts +0 -9
@@ -1,39 +1,75 @@
1
1
  <template>
2
- <FSCol>
3
- <FSTextField
4
- :label="$props.label"
5
- :description="$props.description"
6
- :type="type"
7
- :color="$props.color"
8
- :required="$props.required"
9
- :editable="$props.editable"
10
- :value="innerValue"
11
- @update:value="(value) => innerValue = value"
12
- @keydown.enter="onAdd"
13
- v-bind="$attrs"
14
- >
15
- <template #append-inner>
16
- <FSIcon
17
- class="fs-tag-field-icon"
18
- size="m"
19
- :style="style"
20
- @click="onAdd"
21
- >
22
- mdi-tag-outline
23
- </FSIcon>
24
- </template>
25
- <template v-for="(_, name) in $slots" v-slot:[name]="slotData">
26
- <slot :name="name" v-bind="slotData" />
27
- </template>
28
- </FSTextField>
29
- <FSTagGroup
30
- :tags="$props.value"
31
- :variant="$props.variant"
32
- :color="$props.tagColor"
33
- :editable="$props.editable"
34
- @remove="onRemove"
35
- />
36
- </FSCol>
2
+ <FSCol>
3
+ <FSTextField
4
+ :label="$props.label"
5
+ :description="$props.description"
6
+ :type="type"
7
+ :color="$props.color"
8
+ :required="$props.required"
9
+ :editable="$props.editable"
10
+ :error="messages.length > 0"
11
+ :modelValue="innerValue"
12
+ @update:modelValue="(value) => innerValue = value"
13
+ @keydown.enter="onAdd"
14
+ v-bind="$attrs"
15
+ >
16
+ <template #label>
17
+ <slot name="label">
18
+ <FSRow :wrap="false">
19
+ <FSSpan
20
+ v-if="$props.label"
21
+ class="fs-tag-field-label"
22
+ font="text-overline"
23
+ :style="style"
24
+ >
25
+ {{ $props.label }}
26
+ </FSSpan>
27
+ <FSSpan
28
+ v-if="$props.label && $props.required"
29
+ class="fs-tag-field-label"
30
+ style="margin-left: -8px;"
31
+ font="text-overline"
32
+ :ellipsis="false"
33
+ :style="style"
34
+ >
35
+ *
36
+ </FSSpan>
37
+ <v-spacer style="min-width: 40px;" />
38
+ <FSSpan
39
+ v-if="messages.length > 0"
40
+ class="fs-tag-field-messages"
41
+ font="text-overline"
42
+ :style="style"
43
+ >
44
+ {{ messages.join(", ") }}
45
+ </FSSpan>
46
+ </FSRow>
47
+ </slot>
48
+ </template>
49
+ <template #append-inner>
50
+ <slot name="append-inner">
51
+ <FSIcon
52
+ class="fs-tag-field-icon"
53
+ size="m"
54
+ :style="style"
55
+ @click="onAdd"
56
+ >
57
+ mdi-tag-outline
58
+ </FSIcon>
59
+ </slot>
60
+ </template>
61
+ <template v-for="(_, name) in $slots" v-slot:[name]="slotData">
62
+ <slot :name="name" v-bind="slotData" />
63
+ </template>
64
+ </FSTextField>
65
+ <FSTagGroup
66
+ :tags="$props.modelValue"
67
+ :variant="$props.tagVariant"
68
+ :color="$props.tagColor"
69
+ :editable="$props.editable"
70
+ @remove="onRemove"
71
+ />
72
+ </FSCol>
37
73
  </template>
38
74
 
39
75
  <script lang="ts">
@@ -44,109 +80,132 @@ import { ColorBase } from "@dative-gpi/foundation-shared-components/themes";
44
80
 
45
81
  import FSTextField from "./FSTextField.vue";
46
82
  import FSTagGroup from "./FSTagGroup.vue";
47
- import FSCol from "./FSCol.vue"
83
+ import FSSpan from "./FSSpan.vue";
84
+ import FSCol from "./FSCol.vue";
85
+ import FSRow from "./FSRow.vue";
48
86
 
49
87
  export default defineComponent({
50
- name: "FSTagField",
51
- components: {
52
- FSTextField,
53
- FSTagGroup,
54
- FSCol
88
+ name: "FSTagField",
89
+ components: {
90
+ FSTextField,
91
+ FSTagGroup,
92
+ FSSpan,
93
+ FSCol,
94
+ FSRow
95
+ },
96
+ props: {
97
+ label: {
98
+ type: String,
99
+ required: false,
100
+ default: null
55
101
  },
56
- props: {
57
- label: {
58
- type: String,
59
- required: false,
60
- default: null
61
- },
62
- description: {
63
- type: String,
64
- required: false,
65
- default: null
66
- },
67
- value: {
68
- type: Array as PropType<string[]>,
69
- required: false,
70
- default: () => []
71
- },
72
- variant: {
73
- type: String as PropType<"standard" | "full">,
74
- required: false,
75
- default: "full"
76
- },
77
- color: {
78
- type: String as PropType<ColorBase>,
79
- required: false,
80
- default: ColorBase.Primary
81
- },
82
- tagColor: {
83
- type: String as PropType<ColorBase>,
84
- required: false,
85
- default: ColorBase.Primary
86
- },
87
- required: {
88
- type: Boolean,
89
- required: false,
90
- default: false
91
- },
92
- editable: {
93
- type: Boolean,
94
- required: false,
95
- default: true
96
- }
102
+ description: {
103
+ type: String,
104
+ required: false,
105
+ default: null
106
+ },
107
+ modelValue: {
108
+ type: Array as PropType<string[]>,
109
+ required: false,
110
+ default: () => []
111
+ },
112
+ color: {
113
+ type: String as PropType<ColorBase>,
114
+ required: false,
115
+ default: ColorBase.Dark
116
+ },
117
+ tagVariant: {
118
+ type: String as PropType<"standard" | "full">,
119
+ required: false,
120
+ default: "full"
97
121
  },
98
- emits: ["update:value"],
99
- setup(props, { emit }) {
100
- const { value, editable } = toRefs(props);
122
+ tagColor: {
123
+ type: String as PropType<ColorBase>,
124
+ required: false,
125
+ default: ColorBase.Primary
126
+ },
127
+ required: {
128
+ type: Boolean,
129
+ required: false,
130
+ default: false
131
+ },
132
+ rules: {
133
+ type: Array as PropType<Function[]>,
134
+ required: false,
135
+ default: () => []
136
+ },
137
+ editable: {
138
+ type: Boolean,
139
+ required: false,
140
+ default: true
141
+ }
142
+ },
143
+ emits: ["update:modelValue"],
144
+ setup(props, { emit }) {
145
+ const { modelValue, rules, editable } = toRefs(props);
101
146
 
102
- const innerValue = ref("");
147
+ const innerValue = ref("");
103
148
 
104
- const darks = useColors().getColors(ColorBase.Dark);
149
+ const errors = useColors().getColors(ColorBase.Error);
150
+ const darks = useColors().getColors(ColorBase.Dark);
105
151
 
106
- const style = computed((): {[code: string]: string} & Partial<CSSStyleDeclaration> => {
107
- if (!editable.value) {
108
- return {
109
- "--fs-tag-field-cursor" : "default",
110
- "--fs-tag-field-base-text": darks.light,
111
- "--fs-tag-field-dark-text": darks.light
112
- };
113
- }
114
- return {
115
- "--fs-tag-field-cursor" : "pointer",
116
- "--fs-tag-field-base-text": darks.base,
117
- "--fs-tag-field-dark-text": darks.dark
118
- };
119
- });
152
+ const style = computed((): {[code: string]: string} & Partial<CSSStyleDeclaration> => {
153
+ if (!editable.value) {
154
+ return {
155
+ "--fs-tag-field-cursor" : "default",
156
+ "--fs-tag-field-base-text": darks.light,
157
+ "--fs-tag-field-dark-text": darks.light
158
+ };
159
+ }
160
+ return {
161
+ "--fs-tag-field-cursor" : "pointer",
162
+ "--fs-tag-field-base-text" : darks.base,
163
+ "--fs-tag-field-dark-text" : darks.dark,
164
+ "--fs-tag-field-error-color": errors.base
165
+ };
166
+ });
120
167
 
121
- const onAdd = (): void => {
122
- if (!editable.value) {
123
- return;
124
- }
125
- const tags = value.value ?? [];
126
- if (!innerValue.value.length || tags.includes(innerValue.value)) {
127
- return;
128
- }
129
- emit("update:value", tags.concat(innerValue.value));
130
- innerValue.value = "";
168
+ const messages = computed((): string[] => {
169
+ const messages = [];
170
+ for (const rule of rules.value) {
171
+ const message = rule(props.modelValue);
172
+ if (typeof(message) === "string") {
173
+ messages.push(message);
131
174
  }
175
+ }
176
+ return messages;
177
+ });
132
178
 
133
- const onRemove = (label: string): void => {
134
- if (!editable.value) {
135
- return;
136
- }
137
- const tags = value.value ?? [];
138
- if (!tags.length || !tags.includes(label)) {
139
- return;
140
- }
141
- emit("update:value", tags.filter(t => t !== label));
142
- }
179
+ const onAdd = (): void => {
180
+ if (!editable.value) {
181
+ return;
182
+ }
183
+ const tags = modelValue.value ?? [];
184
+ if (!innerValue.value.length || tags.includes(innerValue.value)) {
185
+ return;
186
+ }
187
+ emit("update:modelValue", tags.concat(innerValue.value));
188
+ innerValue.value = "";
189
+ }
143
190
 
144
- return {
145
- innerValue,
146
- style,
147
- onAdd,
148
- onRemove
149
- };
191
+ const onRemove = (label: string): void => {
192
+ if (!editable.value) {
193
+ return;
194
+ }
195
+ const tags = modelValue.value ?? [];
196
+ if (!tags.length || !tags.includes(label)) {
197
+ return;
198
+ }
199
+ emit("update:modelValue", tags.filter(t => t !== label));
150
200
  }
201
+
202
+ return {
203
+ innerValue,
204
+ messages,
205
+ style,
206
+ onAdd,
207
+ onRemove
208
+ };
209
+ }
151
210
  });
152
211
  </script>
@@ -0,0 +1,207 @@
1
+ <template>
2
+ <FSCol>
3
+ <slot name="label">
4
+ <FSRow :wrap="false">
5
+ <FSSpan
6
+ v-if="$props.label"
7
+ class="fs-text-area-label"
8
+ font="text-overline"
9
+ :style="style"
10
+ >
11
+ {{ $props.label }}
12
+ </FSSpan>
13
+ <FSSpan
14
+ v-if="$props.label && $props.required"
15
+ class="fs-text-area-label"
16
+ style="margin-left: -8px;"
17
+ font="text-overline"
18
+ :ellipsis="false"
19
+ :style="style"
20
+ >
21
+ *
22
+ </FSSpan>
23
+ <v-spacer style="min-width: 40px;" />
24
+ <FSSpan
25
+ v-if="messages.length > 0"
26
+ class="fs-text-area-messages"
27
+ font="text-overline"
28
+ :style="style"
29
+ >
30
+ {{ messages.join(", ") }}
31
+ </FSSpan>
32
+ </FSRow>
33
+ </slot>
34
+ <v-textarea
35
+ :class="classes"
36
+ variant="outlined"
37
+ hide-details
38
+ :style="style"
39
+ :rows="$props.rows"
40
+ :rules="$props.rules"
41
+ :noResize="!$props.resize"
42
+ :autoGrow="$props.autoGrow"
43
+ :readonly="!$props.editable"
44
+ :modelValue="$props.modelValue"
45
+ @update:modelValue="(value) => $emit('update:modelValue', value)"
46
+ v-bind="$attrs"
47
+ >
48
+ <template v-for="(_, name) in $slots" v-slot:[name]="slotData">
49
+ <slot :name="name" v-bind="slotData" />
50
+ </template>
51
+ </v-textarea>
52
+ <slot name="description">
53
+ <FSSpan
54
+ v-if="$props.description"
55
+ class="fs-text-area-description"
56
+ font="text-underline"
57
+ :style="style"
58
+ >
59
+ {{ $props.description }}
60
+ </FSSpan>
61
+ </slot>
62
+ </FSCol>
63
+ </template>
64
+
65
+ <script lang="ts">
66
+ import { computed, defineComponent, PropType, toRefs } from "vue";
67
+
68
+ import { useColors, useBreakpoints } from "@dative-gpi/foundation-shared-components/composables";
69
+ import { ColorBase } from "@dative-gpi/foundation-shared-components/themes";
70
+
71
+ import FSSpan from "./FSSpan.vue";
72
+ import FSCol from "./FSCol.vue";
73
+ import FSRow from "./FSRow.vue";
74
+
75
+ export default defineComponent({
76
+ name: "FSTextArea",
77
+ components: {
78
+ FSSpan,
79
+ FSCol,
80
+ FSRow
81
+ },
82
+ inheritAttrs: false,
83
+ props: {
84
+ label: {
85
+ type: String,
86
+ required: true,
87
+ default: null
88
+ },
89
+ description: {
90
+ type: String,
91
+ required: false,
92
+ default: null
93
+ },
94
+ modelValue: {
95
+ type: String,
96
+ required: false,
97
+ default: null
98
+ },
99
+ color: {
100
+ type: String as PropType<ColorBase>,
101
+ required: false,
102
+ default: ColorBase.Dark
103
+ },
104
+ required: {
105
+ type: Boolean,
106
+ required: false,
107
+ default: false
108
+ },
109
+ rows: {
110
+ type: Number,
111
+ required: false,
112
+ default: 1
113
+ },
114
+ resize: {
115
+ type: Boolean,
116
+ required: false,
117
+ default: true
118
+ },
119
+ autoGrow: {
120
+ type: Boolean,
121
+ required: false,
122
+ default: false
123
+ },
124
+ rules: {
125
+ type: Array as PropType<Function[]>,
126
+ required: false,
127
+ default: () => []
128
+ },
129
+ editable: {
130
+ type: Boolean,
131
+ required: false,
132
+ default: true
133
+ }
134
+ },
135
+ emits: ["update:modelValue"],
136
+ setup(props) {
137
+ const { color, rows, autoGrow, editable } = toRefs(props);
138
+
139
+ const colors = useColors().getColors(color.value);
140
+
141
+ const errors = useColors().getColors(ColorBase.Error);
142
+ const lights = useColors().getColors(ColorBase.Light);
143
+ const darks = useColors().getColors(ColorBase.Dark);
144
+
145
+ const style = computed((): {[code: string]: string} & Partial<CSSStyleDeclaration> => {
146
+ let height: string | undefined = undefined;
147
+ let minHeight: string | undefined = undefined;
148
+ if (!autoGrow.value) {
149
+ const base = useBreakpoints().isMobileSized() ? 30 : 42;
150
+ const row = useBreakpoints().isMobileSized() ? 16 : 20;
151
+ minHeight = `${base}px`;
152
+ if (rows.value > 1) {
153
+ height = `${base + (rows.value - 1) * row}px`;
154
+ }
155
+ else {
156
+ height = `${base}px`;
157
+ }
158
+ }
159
+ if (!editable.value) {
160
+ return {
161
+ "--fs-text-area-cursor" : "default",
162
+ "--fs-text-area-border-color" : lights.base,
163
+ "--fs-text-area-color" : lights.dark,
164
+ "--fs-text-area-active-border-color": lights.base,
165
+ "--fs-text-area-min-height" : minHeight,
166
+ "--fs-text-area-height" : height
167
+ };
168
+ }
169
+ return {
170
+ "--fs-text-area-cursor" : "text",
171
+ "--fs-text-area-border-color" : colors.base,
172
+ "--fs-text-area-color" : darks.base,
173
+ "--fs-text-area-active-border-color": colors.dark,
174
+ "--fs-text-area-error-color" : errors.base,
175
+ "--fs-text-area-error-border-color" : errors.base,
176
+ "--fs-text-area-min-height" : minHeight,
177
+ "--fs-text-area-height" : height
178
+ };
179
+ });
180
+
181
+ const messages = computed(() => {
182
+ const messages = [];
183
+ for (const rule of props.rules) {
184
+ const message = rule(props.modelValue);
185
+ if (typeof(message) === "string") {
186
+ messages.push(message);
187
+ }
188
+ }
189
+ return messages;
190
+ });
191
+
192
+ const classes = computed((): string[] => {
193
+ const classes = ["fs-text-area"];
194
+ if (autoGrow.value) {
195
+ classes.push("fs-text-area-auto-grow");
196
+ }
197
+ return classes;
198
+ });
199
+
200
+ return {
201
+ messages,
202
+ style,
203
+ classes
204
+ };
205
+ }
206
+ });
207
+ </script>