Skip to content

Commit

Permalink
feat(vfg): field-switch to kongponents [khcp-11326] (#1539)
Browse files Browse the repository at this point in the history
  • Loading branch information
kaiarrowood authored Aug 8, 2024
1 parent 5fb45cd commit 0c55877
Show file tree
Hide file tree
Showing 7 changed files with 280 additions and 216 deletions.
13 changes: 12 additions & 1 deletion packages/core/forms/sandbox/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,28 @@ const fieldSchema = {
id: 'is_friendly',
label: 'Is Friendly',
},
// FieldSwitch
{
type: 'switch',
model: 'is_cute',
label: 'Is Cute',
textOn: 'Cute',
textOff: 'Not Cute',
styleClasses: 'field-switch hide-label',
},
],
}
const fieldModelDefault = ref({
cat_name: 'TK Meowstersmith',
is_friendly: true,
is_cute: true,
})
const fieldModelModified = ref({
cat_name: 'TK Meowstersmith, Esq.',
cat_name: 'BratCat',
is_friendly: false,
is_cute: false,
})
</script>

Expand Down
49 changes: 0 additions & 49 deletions packages/core/forms/src/components/FormGenerator.vue
Original file line number Diff line number Diff line change
Expand Up @@ -516,55 +516,6 @@ export default {
font-style: italic;
} // .hint
/* Toggle Switch
========================================================================== */
.field-switch {
.field-wrap {
input[type="checkbox"] {
position: absolute;
&:checked ~ .label {
background-color: $kui-color-background-primary;
box-shadow: none;
}
&:checked ~ .handle {
left: calc(100% - 22px);
}
}
label {
box-shadow: none;
height: 24px;
margin: 0;
width: 44px;
}
}
.label {
box-shadow: none;
&:before,
&:after {
color: rgba(0, 0, 0, 0.7);
font-size: 14px;
font-weight: normal;
left: 18px;
margin-left: 42px;
text-shadow: none;
text-transform: none;
width: max-content;
}
}
.handle {
background: #fff;
box-shadow: none;
height: 20px;
left: 2px;
top: 2px;
width: 20px;
&:before {
background: none;
box-shadow: none;
}
}
}
.kong-form-new-element-button-label {
margin-bottom: $kui-space-80!important;
margin-top: $kui-space-80!important;
Expand Down
209 changes: 85 additions & 124 deletions packages/core/forms/src/components/fields/FieldSwitch.vue
Original file line number Diff line number Diff line change
@@ -1,137 +1,98 @@
<template lang="pug">
label
input(type="checkbox", v-model="value", :autocomplete="schema.autocomplete", :disabled="disabled || null", :name="schema.inputName", :id="getFieldID(schema)")
span.label(:data-on="schema.textOn || 'On'", :data-off="schema.textOff || 'Off'", :for="getFieldID(schema)")
span.handle
<template>
<div class="form-field-wrapper">
<KInputSwitch
:id="getFieldID(schema)"
v-model="inputValue"
:autocomplete="schema.autocomplete"
:class="schema.fieldClasses"
:disabled="disabled || undefined"
:label="inputValue ? schema.textOn || t('vfg.labels.on') : schema.textOff || t('vfg.labels.off')"
:name="schema.inputName"
/>
</div>
</template>

<script>
import abstractField from './abstractField'
<script lang="ts" setup>
import { toRefs, type PropType } from 'vue'
import { createI18n } from '@kong-ui-public/i18n'
import composables from '../../composables'
import english from '../../locales/en.json'
export default {
mixins: [abstractField],
const props = defineProps({
disabled: {
type: Boolean,
default: false,
},
formOptions: {
type: Object as PropType<Record<string, any>>,
default: () => undefined,
},
model: {
type: Object as PropType<Record<string, any>>,
default: () => undefined,
},
schema: {
type: Object as PropType<Record<string, any>>,
required: true,
},
vfg: {
type: Object,
required: true,
},
/**
* TODO: stronger type
* TODO: pass this down to KInput error and errorMessage
*/
errors: {
type: Array,
default: () => [],
},
hint: {
type: String,
default: '',
},
})
methods: {
formatValueToField(value) {
if (value != null && this.schema.valueOn) return value === this.schema.valueOn
const emit = defineEmits<{
(event: 'modelUpdated', value: any, model: Record<string, any>): void
}>()
return value
},
const { t } = createI18n<typeof english>('en-us', english)
formatValueToModel(value) {
if (value != null && this.schema.valueOn) {
if (value) return this.schema.valueOn
else return this.schema.valueOff
}
const formatValueToField = (value: boolean): boolean => {
if (value != null && props.schema.valueOn) {
return value === props.schema.valueOn
}
return value
},
},
return value
}
</script>

<style lang="scss">
$field-switch-width: 120px;
$field-switch-height: 30px;
.vue-form-generator .field-switch {
.field-wrap label {
border-radius: calc(#{$field-switch-height} / 2);
box-shadow: inset 0 -1px white, inset 0 1px 1px rgba(0, 0, 0, 0.05);
cursor: pointer;
display: block;
height: $field-switch-height;
margin: 0 10px 10px 0;
padding: 0;
position: relative;
width: $field-switch-width;
}
input {
left: 0;
opacity: 0;
position: absolute;
top: 0;
}
.label {
background: #eceeef;
border-radius: inherit;
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.12), inset 0 0 2px rgba(0, 0, 0, 0.15);
display: block;
font-size: 10px;
height: inherit;
position: relative;
text-transform: uppercase;
width: 100%;
}
.label:before,
.label:after {
line-height: 1;
margin-top: -7px;
position: absolute;
top: 50%;
-webkit-transition: inherit;
-moz-transition: inherit;
-o-transition: inherit;
transition: inherit;
}
.label:before {
color: #aaaaaa;
content: attr(data-off);
right: 11px;
text-shadow: 0 1px rgba(255, 255, 255, 0.5);
}
.label:after {
color: #ffffff;
content: attr(data-on);
left: 11px;
opacity: 0;
text-shadow: 0 1px rgba(0, 0, 0, 0.2);
}
input:checked ~ .label {
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15), inset 0 0 3px rgba(0, 0, 0, 0.2);
}
input:checked ~ .label:before {
opacity: 0;
}
input:checked ~ .label:after {
opacity: 1;
}
const formatValueToModel = (value: boolean): boolean => {
if (value != null && props.schema.valueOn) {
if (value) {
return props.schema.valueOn
}
.handle {
background: linear-gradient(to bottom, #ffffff 40%, #f0f0f0);
background-image: -webkit-linear-gradient(top, #ffffff 40%, #f0f0f0);
border-radius: 100%;
box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.2);
height: $field-switch-height - 2px;
left: 1px;
position: absolute;
top: 1px;
width: $field-switch-height - 2px;
}
.handle:before {
background: linear-gradient(to bottom, #eeeeee, #ffffff);
background-image: -webkit-linear-gradient(top, #eeeeee, #ffffff);
border-radius: 6px;
box-shadow: inset 0 1px rgba(0, 0, 0, 0.02);
content: "";
height: 12px;
left: 50%;
margin: -6px 0 0 -6px;
position: absolute;
top: 50%;
width: 12px;
}
input:checked ~ .handle {
box-shadow: -1px 1px 5px rgba(0, 0, 0, 0.2);
left: $field-switch-width - ($field-switch-height - 1px);
left: calc(100% - (#{$field-switch-height} - 1px));
return props.schema.valueOff
}
/* Transition
========================== */
.label,
.handle {
transition: all 0.3s ease;
}
return value
}
</style>
const propsRefs = toRefs(props)
const { getFieldID, value: inputValue, clearValidationErrors } = composables.useAbstractFields({
model: propsRefs.model,
schema: props.schema,
formOptions: props.formOptions,
formatValueToField,
formatValueToModel,
emitModelUpdated: (data: { value: any, model: Record<string, any> }): void => {
emit('modelUpdated', data.value, data.model)
},
})
defineExpose({
clearValidationErrors,
})
</script>
Loading

0 comments on commit 0c55877

Please sign in to comment.