-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: vpn deploy view, detail view wip
- Loading branch information
Showing
7 changed files
with
567 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
<script setup> | ||
import LoadingSpinner from '@/components/icons/LoadingSpinner.vue' | ||
import ValidationError from '@/components/ValidationError.vue' | ||
import { required } from '@vuelidate/validators' | ||
import useVuelidate from '@vuelidate/core' | ||
import { CheckIcon, PencilIcon, XIcon } from '@heroicons/vue/outline' | ||
import { computed, defineEmits, defineProps, reactive, ref, watchEffect } from 'vue' | ||
const emit = defineEmits(['change']) | ||
const props = defineProps({ | ||
busy: Boolean, | ||
disabled: Boolean, | ||
placeholder: String, | ||
validation: [Function, Object], | ||
value: String | ||
}) | ||
const formState = reactive({ | ||
newValue: '' | ||
}) | ||
const v$ = useVuelidate({ newValue: props.validation || { required } }, formState) | ||
const canSave = computed(() => !props.disabled && !v$.value.$invalid) | ||
const editing = ref(false) | ||
const inputRef = ref(null) | ||
function cancelEdit() { | ||
editing.value = false | ||
reset() | ||
} | ||
function reset() { | ||
v$.value.newValue.$model = props.value || '' | ||
v$.value.$reset() | ||
} | ||
function startEdit() { | ||
reset() | ||
editing.value = true | ||
} | ||
async function stopEdit() { | ||
if (!await v$.value.$validate()) { | ||
console.log(v$.value.$errors) | ||
return | ||
} | ||
emit('change', v$.value.newValue.$model) | ||
editing.value = false | ||
} | ||
watchEffect(() => { | ||
if (inputRef.value) { | ||
inputRef.value.focus() | ||
} | ||
}) | ||
</script> | ||
|
||
<template> | ||
<div class="editable-title__container flex"> | ||
<div v-if="editing"> | ||
<input | ||
v-model="v$.newValue.$model" | ||
@keypress.enter="stopEdit" | ||
class="editable-title__value" | ||
:placeholder="placeholder" | ||
ref="inputRef" | ||
type="text" | ||
/> | ||
<ValidationError :errors="v$.newValue.$errors" /> | ||
</div> | ||
<h1 v-else class="w-max mb-0">{{ busy ? v$.newValue.$model : value }}</h1> | ||
|
||
<div class="mt-3"> | ||
<button v-if="busy" class="ml-2" disabled> | ||
<LoadingSpinner /> | ||
</button> | ||
<button v-else-if="editing" class="ml-2" @click="stopEdit" :disabled="!canSave"> | ||
<CheckIcon class="button__icon text-green hover:text-green-600" /> | ||
</button> | ||
<button v-else class="ml-2" @click="startEdit" :disabled="disabled"> | ||
<PencilIcon class="button__icon text-gray-400 hover:text-green" /> | ||
</button> | ||
|
||
<button v-if="!busy && editing" @click="cancelEdit" class="ml-2"> | ||
<XIcon class="button__icon text-red hover:text-red-600" /> | ||
</button> | ||
</div> | ||
</div> | ||
</template> | ||
|
||
<style> | ||
.editable-title__value { | ||
@apply bg-transparent text-3xl text-gray-600 border-b border-gray-400 w-full; | ||
@apply focus:outline-none focus:border-green focus:text-green; | ||
} | ||
.editable-title__container .button__icon { | ||
@apply w-5 ml-1; | ||
} | ||
.editable-title__container button:disabled, | ||
.editable-title__container button:disabled .button__icon { | ||
@apply text-gray-400 hover:no-underline; | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
<script setup> | ||
import 'vue-slider-component/theme/antd.css' | ||
import VueSlider from 'vue-slider-component' | ||
import { defineEmits, defineModel, defineProps } from 'vue' | ||
defineProps({ | ||
disabled: Boolean, | ||
formatter: Function, | ||
marks: Object, | ||
max: Number, | ||
min: Number, | ||
title: String, | ||
tooltip: String | ||
}) | ||
const emit = defineEmits(['change']) | ||
const value = defineModel() | ||
</script> | ||
|
||
<template> | ||
<div class="slider__container" :class="{ disabled }"> | ||
<span v-if="title" class="slider__title">{{ title }}</span> | ||
<vue-slider | ||
:contained="true" | ||
:disabled="disabled" | ||
:dot-style="{ background: '#4ecd5f', boxShadow: '0 0 2px 1px #eee', border: 'none' }" | ||
:dotSize="20" | ||
:label-style="{ color: '#999', fontSize: '12px' }" | ||
:marks="marks" | ||
:max="max" | ||
:min="min" | ||
:process-style="{ background: '#4ecd5f' }" | ||
:step-active-style="{ background: '#fff', opacity: '1', border: 'none', boxShadow: 'rgba(0, 0, 0, 0.24) 0px 3px 8px' }" | ||
:tooltip="tooltip || 'hover'" | ||
:tooltip-formatter="formatter" | ||
:tooltip-style="{ background: '#4ecd5f', borderColor: '#4ecd5f' }" | ||
adsorb | ||
tooltipPlacement="top" | ||
v-model=value | ||
width="100%" | ||
@change="val => emit('change', val)" | ||
/> | ||
</div> | ||
</template> | ||
|
||
<style> | ||
.slider__container { | ||
@apply relative flex space-x-3 items-start justify-center pr-5 pl-2 pt-14 pb-8 border border-gray-300 rounded-md; | ||
} | ||
.slider__container.disabled { | ||
@apply cursor-not-allowed opacity-50; | ||
} | ||
.slider__title { | ||
@apply absolute top-0 inline-block px-3 text-gray-500 transform -translate-y-1/2 bg-white; | ||
} | ||
</style> |
Oops, something went wrong.