Skip to content

Commit 74fbc70

Browse files
[DEV] Adding more prop functionality and changing to use v-radio-group
1 parent 6fbce90 commit 74fbc70

File tree

2 files changed

+188
-51
lines changed

2 files changed

+188
-51
lines changed

src/plugin/components/fields/VSFFancyRadio/VSFFancyRadio.vue

Lines changed: 161 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,76 @@
11
<template>
22
<div>
3-
<FieldLabel
4-
:label="field.label"
5-
:required="fieldRequired"
6-
/>
7-
8-
<div class="vsf-fancy-radio__container">
9-
<div class="v-input__control vsf-fancy-radio__control">
10-
<template
11-
v-for="option in field?.options"
12-
:key="option.value"
3+
<div
4+
:id="field?.groupId"
5+
class="vsf-fancy-radio__container"
6+
>
7+
<Field
8+
v-slot="{ errorMessage, validate }"
9+
v-model="modelValue"
10+
:name="field.name"
11+
type="radio"
12+
:validate-on-model-update="true"
13+
>
14+
<v-radio-group
15+
v-model="modelValue"
16+
class="vsf-fancy-radio__control"
17+
:direction="field?.direction"
18+
:error="hasErrors"
19+
:error-messages="errorMessage || field?.errorMessages"
20+
:hideDetails="field?.hideDetails || settings?.hideDetails"
21+
:hint="field?.hint"
22+
:inline="field?.inline"
23+
:max-errors="field?.maxErrors"
24+
:max-width="field?.maxWidth"
25+
:messages="field?.messages"
26+
:min-width="field?.minWidth"
27+
:multiple="field?.multiple"
28+
:persistentHint="field?.persistentHint"
29+
:theme="field?.theme"
1330
>
31+
<template #label>
32+
<FieldLabel
33+
:label="field.label"
34+
:required="fieldRequired"
35+
/>
36+
</template>
37+
1438
<div
39+
v-for="option in field?.options"
40+
:key="option.value"
1541
class="vsf-fancy-radio__field v-field"
1642
:class="{
1743
...fieldClasses,
18-
[`vsf-fancy-radio__field--variant-${fieldVariant}-focused`]: isFocused === option.value,
44+
[`vsf-fancy-radio__field-variant-${fieldVariant}-focused`]: isFocused === option.value,
1945
}"
2046
:style="fieldStyle"
2147
>
22-
<Field
23-
v-slot="{ errors, errorMessage, validate }"
48+
<input
49+
:id="option?.id"
2450
v-model="modelValue"
51+
class="vsf-fancy-radio__input"
52+
:class="{
53+
'vsf-fancy-radio__input_checked': modelValue === option.value || (Array.isArray(modelValue) ? modelValue.includes(option.value) : false),
54+
'vsf-fancy-radio__input_error': hasErrors || errorMessage || field?.errorMessages,
55+
}"
56+
:disabled="(field.disabled as boolean)"
2557
:name="field.name"
2658
type="radio"
27-
:validate-on-model-update="true"
28-
>
29-
<input
30-
:id="`vsf-radio-${field.name}-${option.value}`"
31-
v-model="modelValue"
32-
class="vsf-fancy-radio__input"
33-
:class="{
34-
'vsf-fancy-radio__input_checked': modelValue === option.value,
35-
'vsf-fancy-radio__input_error': errors.length > 0,
36-
}"
37-
:error="errorMessage ? errorMessage?.length > 0 : false"
38-
:error-messages="errorMessage"
39-
:name="field.name"
40-
type="radio"
41-
:value="option.value"
42-
@blur="onActions(validate, 'blur')"
43-
@change="onActions(validate, 'change')"
44-
@click="onActions(validate, 'click')"
45-
@input="onActions(validate, 'input')"
46-
/>
47-
</Field>
59+
:value="option.value"
60+
:width="field?.width"
61+
@blur="onActions(validate, 'blur')"
62+
@change="onActions(validate, 'change')"
63+
@input="onActions(validate, 'input')"
64+
/>
4865

4966
<label
5067
:class="{
5168
...labelClasses,
52-
[`vsf-fancy-radio__label--variant-${fieldVariant}-focused`]: isFocused === option.value,
69+
[`vsf-fancy-radio__label-variant-${fieldVariant}-focused`]: isFocused === option.value,
5370
}"
5471
:for="`vsf-radio-${field.name}-${option.value}`"
5572
:style="labelStyle"
73+
@click="onActions(validate, 'click', option.value)"
5674
@mousedown="onFocus(option.value)"
5775
@mouseleave="onFocus(null)"
5876
@mouseup="onFocus(null)"
@@ -69,9 +87,10 @@
6987
></div>
7088
</label>
7189
</div>
72-
</template>
73-
</div>
90+
</v-radio-group>
91+
</Field>
7492
</div>
93+
7594
</div>
7695
</template>
7796

@@ -96,8 +115,29 @@ const fieldRequired = computed(() => {
96115
});
97116
98117
118+
if (modelValue?.value == null) {
119+
modelValue.value = field?.multiple ? [] : null;
120+
}
121+
99122
// ------------------------- Validate On Actions //
100-
async function onActions(validate: FieldValidateResult, action: ValidateAction): Promise<void> {
123+
async function onActions(validate: FieldValidateResult, action: ValidateAction, value?: unknown): Promise<void> {
124+
// TODO: Check the other validate on states //
125+
126+
if (!field?.disabled && value) {
127+
if (field?.multiple) {
128+
if (modelValue.value.includes(value)) {
129+
const index = modelValue.value.indexOf(value);
130+
modelValue.value.splice(index, 1);
131+
}
132+
else {
133+
modelValue.value.push(value);
134+
}
135+
}
136+
else {
137+
modelValue.value = value;
138+
}
139+
}
140+
101141
useOnActions({
102142
action,
103143
emit,
@@ -108,6 +148,15 @@ async function onActions(validate: FieldValidateResult, action: ValidateAction):
108148
}
109149
110150
151+
const hasErrors = computed(() => {
152+
let err = field?.error;
153+
154+
err = field?.errorMessages ? field.errorMessages.length > 0 : err;
155+
156+
return err;
157+
});
158+
159+
111160
// -------------------------------------------------- Properties //
112161
const densityHeight = {
113162
comfortable: '48px',
@@ -138,7 +187,10 @@ const fieldHeight = computed(() => {
138187
const fieldStyle = computed<CSSProperties>(() => {
139188
const styles = {
140189
'height': fieldHeight.value,
190+
'max-width': field?.maxWidth ?? '100%',
141191
'min-height': fieldHeight.value,
192+
'min-width': field.minWidth ?? 'fit-content',
193+
'width': field?.width ?? '100px',
142194
};
143195
144196
return styles;
@@ -147,13 +199,16 @@ const fieldStyle = computed<CSSProperties>(() => {
147199
const labelStyle = computed<CSSProperties>(() => {
148200
const styles = {
149201
'min-width': '100px',
150-
'width': field?.width ?? '100px',
202+
'width': field?.minWidth ?? field?.maxWidth ?? field?.width ?? '100px',
151203
};
152204
153205
return styles;
154206
});
155207
156208
209+
const gap = ref(field.gap ?? '10px');
210+
211+
157212
// -------------------------------------------------- Classes //
158213
const fieldOverlayClasses = computed(() => {
159214
return {
@@ -182,8 +237,10 @@ const fieldTextClasses = computed(() => {
182237
183238
const fieldClasses = computed(() => {
184239
return {
240+
'vsf-fancy-radio__field-disabled': field?.disabled,
241+
'vsf-fancy-radio__field-flat': field?.flat,
185242
[`v-field--variant-${fieldVariant.value}`]: true,
186-
[`vsf-fancy-radio__field--variant-${fieldVariant.value}`]: true,
243+
[`vsf-fancy-radio__field-variant-${fieldVariant.value}`]: true,
187244
};
188245
});
189246
@@ -192,8 +249,7 @@ const labelClasses = computed(() => {
192249
'pa-1': field?.density === 'compact',
193250
'pa-4': field?.density !== 'compact',
194251
'vsf-fancy-radio__label': true,
195-
[`vsf-fancy-radio__label--variant-${fieldVariant.value}`]: true,
196-
// [`vsf-fancy-radio__label-variant-${variant}_${isFocused.value}`]: true,
252+
[`vsf-fancy-radio__label-variant-${fieldVariant.value}`]: true,
197253
};
198254
});
199255
@@ -210,6 +266,7 @@ function onFocus(value: any) {
210266
:root {
211267
--vsf-field-border-radius: 4px;
212268
--vsf-field-border-opacity: 0.04;
269+
--vsf-field-disabled-opacity: 0.25;
213270
}
214271
</style>
215272

@@ -218,7 +275,6 @@ function onFocus(value: any) {
218275
&__container {
219276
align-items: center;
220277
display: flex;
221-
gap: 10px;
222278
height: fit-content;
223279
justify-content: center;
224280
position: relative;
@@ -227,16 +283,76 @@ function onFocus(value: any) {
227283
&__control {
228284
gap: 10px;
229285
min-height: fit-content !important;
286+
min-width: fit-content !important;
287+
288+
:deep(.v-input__control) {
289+
align-items: center;
290+
display: flex;
291+
justify-content: center;
292+
293+
.v-selection-control-group {
294+
flex-direction: row !important;
295+
gap: v-bind(gap) !important;
296+
justify-content: center !important;
297+
padding-inline-start: 0 !important;
298+
}
299+
300+
.v-label {
301+
margin-inline-start: 0 !important;
302+
}
303+
}
304+
305+
:deep(.v-input__details) {
306+
.v-messages {
307+
align-items: center;
308+
display: flex;
309+
flex-direction: column;
310+
justify-content: center;
311+
}
312+
}
313+
314+
&.v-input--vertical {
315+
display: flex;
316+
flex-direction: column;
317+
318+
:deep(.v-input__control) {
319+
.v-selection-control-group {
320+
flex-direction: column !important;
321+
}
322+
}
323+
}
230324
}
231325
232326
// -------------------------------------------------- Field //
233327
&__field {
234328
border-radius: var(--vsf-field-border-radius);
329+
display: block !important;
330+
flex-grow: unset;
235331
grid-area: unset;
236332
min-height: fit-content !important;
237333
334+
&-disabled {
335+
.vsf-fancy-radio__overlay {
336+
opacity: var(--vsf-field-disabled-opacity) !important;
337+
}
338+
339+
.vsf-fancy-radio__label {
340+
cursor: default !important;
341+
342+
&:hover {
343+
.vsf-fancy-radio__overlay {
344+
opacity: var(--vsf-field-disabled-opacity) !important;
345+
}
346+
}
347+
}
348+
}
349+
350+
&-flat {
351+
--vsf-field-border-radius: 0;
352+
}
353+
238354
// ? -------------------------------------------------- Variants //
239-
&--variant {
355+
&-variant {
240356
241357
// ? ------------------------- Filled //
242358
&-filled {
@@ -334,7 +450,7 @@ function onFocus(value: any) {
334450
}
335451
336452
// ? -------------------------------------------------- Variants //
337-
&--variant {
453+
&-variant {
338454
// ? ------------------------- Filled //
339455
&-filled {
340456
border-radius: var(--vsf-field-border-radius) var(--vsf-field-border-radius) 0 0;

src/plugin/components/fields/VSFFancyRadio/index.ts

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,42 @@
11
import type {
22
Field,
3+
GlobalDensity,
4+
GlobalVariant,
35
SharedProps,
46
} from '@/plugin/types';
7+
import type { VRadio, VRadioGroup } from 'vuetify/components';
58
import type VSFFancyRadio from './VSFFancyRadio.vue';
69

710

8-
interface InternalField extends Omit<Field,
9-
'inline' | 'inlineSpacing' | 'labelPositionLeft'
10-
> {
11+
export interface RadioGroupProps {
12+
appendIcon?: VRadioGroup['appendIcon'];
13+
direction?: VRadioGroup['direction'];
14+
error?: VRadioGroup['error'];
15+
hideDetails?: VRadioGroup['hideDetails'];
16+
hint?: VRadioGroup['hint'];
17+
inline?: VRadioGroup['inline'];
18+
groupId?: VRadioGroup['id'];
19+
maxErrors?: VRadioGroup['maxErrors'];
20+
maxWidth?: VRadioGroup['maxWidth'];
21+
minWidth?: VRadioGroup['minWidth'];
22+
messages?: VRadioGroup['messages'];
23+
persistentHint?: VRadioGroup['persistentHint'];
24+
prependIcon?: VRadioGroup['prependIcon'];
25+
theme?: VRadioGroup['theme'];
26+
width?: VRadioGroup['width'];
27+
}
28+
29+
interface InternalField extends Field, RadioGroupProps {
30+
flat?: boolean;
1131
focused?: boolean;
32+
gap?: string;
1233
height?: string;
13-
width?: string;
14-
variant?: 'underlined' | 'outlined' | 'filled' | 'solo' | 'solo-inverted' | 'solo-filled' | 'plain';
34+
multiple?: VRadio['multiple'];
35+
variant?: Omit<GlobalVariant, 'solo-inverted'>;
1536
}
1637

1738
export interface VSFFancyRadioProps extends SharedProps {
18-
density?: Field['density'] | 'expanded' | 'oversized';
39+
density?: GlobalDensity | 'expanded' | 'oversized';
1940
field: InternalField;
2041
}
2142

0 commit comments

Comments
 (0)