Skip to content

Commit

Permalink
feat: vpn deploy view, detail view wip
Browse files Browse the repository at this point in the history
  • Loading branch information
annybs committed Jan 28, 2025
1 parent 62b9a31 commit 5a2fedb
Show file tree
Hide file tree
Showing 7 changed files with 567 additions and 3 deletions.
109 changes: 109 additions & 0 deletions src/layout/EditableTitle.vue
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>
60 changes: 60 additions & 0 deletions src/layout/Slider.vue
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>
Loading

0 comments on commit 5a2fedb

Please sign in to comment.