- second commit

This commit is contained in:
Kaimbacher 2023-03-17 16:13:37 +01:00
parent 4fc3bb0a01
commit 59a99ff3c8
61 changed files with 2625 additions and 1182 deletions

View file

@ -2,8 +2,8 @@
// import { reactive, computed } from 'vue';
// import { usePage } from '@inertiajs/vue3'
// import { usePage } from '@inertiajs/inertia-vue3';
import { LayoutService } from '@/Stores/layout.js';
import menu from '@/menu.js'
import { LayoutService } from '@/Stores/layout';
import menu from '@/menu'
import AsideMenuLayer from '@/Components/AsideMenuLayer.vue';
import OverlayLayer from '@/Components/OverlayLayer.vue';

View file

@ -3,7 +3,7 @@ import { ref, computed } from 'vue';
import { Link } from '@inertiajs/vue3';
// import { Link } from '@inertiajs/inertia-vue3';
import { StyleService } from '@/Stores/style.js';
import { StyleService } from '@/Stores/style';
import { mdiMinus, mdiPlus } from '@mdi/js';
import { getButtonColor } from '@/colors.js';
import BaseIcon from '@/Components/BaseIcon.vue';

View file

@ -4,8 +4,8 @@ import { router } from '@inertiajs/vue3'
import { stardust } from '@eidellev/adonis-stardust/client';
import { mdiLogout, mdiClose } from '@mdi/js';
import { computed } from 'vue';
import { LayoutService } from '@/Stores/layout.js';
import { StyleService } from '@/Stores/style.js';
import { LayoutService } from '@/Stores/layout';
import { StyleService } from '@/Stores/style';
import AsideMenuList from '@/Components/AsideMenuList.vue';
import AsideMenuItem from '@/Components/AsideMenuItem.vue';
import BaseIcon from '@/Components/BaseIcon.vue';

View file

@ -2,28 +2,28 @@
import AsideMenuItem from '@/Components/AsideMenuItem.vue';
defineProps({
isDropdownList: Boolean,
menu: {
type: Object,
default: () => {}
}
})
isDropdownList: Boolean,
menu: {
type: Object,
default: () => {},
},
});
const emit = defineEmits(['menu-click'])
const emit = defineEmits(['menu-click']);
const menuClick = (event, item) => {
emit('menu-click', event, item)
}
emit('menu-click', event, item);
};
</script>
<template>
<ul>
<AsideMenuItem
v-for="(item, index) in menu"
:key="index"
v-bind:item="item"
:is-dropdown-list="isDropdownList"
@menu-click="menuClick"
/>
</ul>
<ul>
<AsideMenuItem
v-for="(item, index) in menu"
:key="index"
v-bind:item="item"
:is-dropdown-list="isDropdownList"
@menu-click="menuClick"
/>
</ul>
</template>

View file

@ -1,111 +1,81 @@
<script setup>
import { mdiCog } from '@mdi/js'
import { computed, useSlots } from 'vue'
import BaseIcon from '@/Components/BaseIcon.vue'
import { mdiCog } from '@mdi/js';
import { computed, useSlots } from 'vue';
import BaseIcon from '@/Components/BaseIcon.vue';
const props = defineProps({
title: {
type: String,
default: null
},
icon: {
type: String,
default: null
},
headerIcon: {
type: String,
default: null
},
rounded: {
type: String,
default: 'rounded-xl'
},
hasTable: Boolean,
empty: Boolean,
form: Boolean,
hoverable: Boolean,
modal: Boolean
})
title: {
type: String,
default: null,
},
icon: {
type: String,
default: null,
},
headerIcon: {
type: String,
default: null,
},
rounded: {
type: String,
default: 'rounded-xl',
},
hasFormData: Boolean,
empty: Boolean,
form: Boolean,
hoverable: Boolean,
modal: Boolean,
});
const emit = defineEmits(['header-icon-click', 'submit'])
const emit = defineEmits(['header-icon-click', 'submit']);
const is = computed(() => props.form ? 'form' : 'div')
const is = computed(() => (props.form ? 'form' : 'div'));
const slots = useSlots()
const slots = useSlots();
const footer = computed(() => slots.footer && !!slots.footer())
const footer = computed(() => slots.footer && !!slots.footer());
const componentClass = computed(() => {
const base = [
props.rounded,
props.modal ? 'dark:bg-slate-900' : 'dark:bg-slate-900/70'
]
const base = [props.rounded, props.modal ? 'dark:bg-slate-900' : 'dark:bg-slate-900/70'];
if (props.hoverable) {
base.push('hover:shadow-lg transition-shadow duration-500')
}
if (props.hoverable) {
base.push('hover:shadow-lg transition-shadow duration-500');
}
return base
})
return base;
});
const computedHeaderIcon = computed(() => props.headerIcon ?? mdiCog)
const computedHeaderIcon = computed(() => props.headerIcon ?? mdiCog);
const headerIconClick = () => {
emit('header-icon-click')
}
emit('header-icon-click');
};
const submit = e => {
emit('submit', e)
}
const submit = (e) => {
emit('submit', e);
};
</script>
<template>
<component
:is="is"
:class="componentClass"
class="bg-white flex flex-col"
@submit="submit"
>
<header
v-if="title"
class="flex items-stretch border-b border-gray-100 dark:border-slate-800"
>
<div
class="flex items-center py-3 grow font-bold"
:class="[ icon ? 'px-4' : 'px-6' ]"
>
<BaseIcon
v-if="icon"
:path="icon"
class="mr-3"
/>
{{ title }}
</div>
<button
class="flex items-center py-3 px-4 justify-center ring-blue-700 focus:ring"
@click="headerIconClick"
>
<BaseIcon :path="computedHeaderIcon" />
</button>
</header>
<div
v-if="empty"
class="text-center py-24 text-gray-500 dark:text-slate-400"
>
<p>Nothing's here</p>
</div>
<div
v-else
class="flex-1"
:class="{'p-6':!hasTable}"
>
<slot />
</div>
<div
v-if="footer"
class="p-6 border-t border-gray-100 dark:border-slate-800"
>
<slot name="footer" />
</div>
</component>
<component :is="is" :class="componentClass" class="bg-white flex flex-col" @submit="submit">
<header v-if="title" class="flex items-stretch border-b border-gray-100 dark:border-slate-800">
<div class="flex items-center py-3 grow font-bold" :class="[icon ? 'px-4' : 'px-6']">
<BaseIcon v-if="icon" :path="icon" class="mr-3" />
{{ title }}
</div>
<button class="flex items-center py-3 px-4 justify-center ring-blue-700 focus:ring" @click="headerIconClick">
<BaseIcon :path="computedHeaderIcon" />
</button>
</header>
<div v-if="empty" class="text-center py-24 text-gray-500 dark:text-slate-400">
<p>Nothing's here</p>
</div>
<!-- <div v-else class="flex-1" :class="{'p-6':!hasTable}"> -->
<div v-else class="flex-1" :class="[!hasFormData && 'p-6']">
<slot />
</div>
<div v-if="footer" class="p-6 border-t border-gray-100 dark:border-slate-800">
<slot name="footer" />
</div>
</component>
</template>

View file

@ -1,56 +1,62 @@
<script setup>
import { computed } from 'vue'
import { mdiClose } from '@mdi/js'
import BaseButton from '@/Components/BaseButton.vue'
import BaseButtons from '@/Components/BaseButtons.vue'
import CardBox from '@/Components/CardBox.vue'
import OverlayLayer from '@/Components/OverlayLayer.vue'
<script setup lang="ts">
import { computed } from 'vue';
import { mdiClose } from '@mdi/js';
import BaseButton from '@/Components/BaseButton.vue';
import BaseButtons from '@/Components/BaseButtons.vue';
import CardBox from '@/Components/CardBox.vue';
import OverlayLayer from '@/Components/OverlayLayer.vue';
const props = defineProps({
title: {
type: String,
default: null
default: null,
},
largeTitle: {
type: String,
default: null
default: null,
},
button: {
type: String,
default: 'info'
default: 'info',
},
buttonLabel: {
type: String,
default: 'Done'
default: 'Done',
},
hasCancel: Boolean,
modelValue: {
type: [String, Number, Boolean],
default: null
}
})
default: null,
},
});
const emit = defineEmits(['update:modelValue', 'cancel', 'confirm'])
const emit = defineEmits(['update:modelValue', 'cancel', 'confirm']);
const value = computed({
get: () => props.modelValue,
set: value => emit('update:modelValue', value)
})
set: (value) => emit('update:modelValue', value),
});
const confirmCancel = (mode) => {
value.value = false;
emit(mode);
}
};
const confirm = () => confirmCancel('confirm')
const confirm = () => confirmCancel('confirm');
const cancel = () => confirmCancel('cancel')
const cancel = () => confirmCancel('cancel');
</script>
<template>
<OverlayLayer v-show="value" @overlay-click="cancel">
<CardBox v-show="value" :title="title" class="shadow-lg max-h-modal w-11/12 md:w-3/5 lg:w-2/5 xl:w-4/12 z-50"
:header-icon="mdiClose" modal @header-icon-click="cancel">
<CardBox
v-show="value"
:title="title"
class="shadow-lg max-h-modal w-11/12 md:w-3/5 lg:w-2/5 xl:w-4/12 z-50"
:header-icon="mdiClose"
modal
@header-icon-click="cancel"
>
<div class="space-y-3">
<h1 v-if="largeTitle" class="text-2xl">
{{ largeTitle }}

View file

@ -3,66 +3,71 @@ import { computed, ref, onMounted, onBeforeUnmount } from 'vue';
import { MainService } from '@/Stores/main';
import FormControlIcon from '@/Components/FormControlIcon.vue';
const props = defineProps({
name: {
type: String,
default: null,
},
id: {
type: String,
default: null,
},
autocomplete: {
type: String,
default: null,
},
placeholder: {
type: String,
default: null,
},
inputmode: {
type: String,
default: null,
},
icon: {
type: String,
default: null,
},
options: {
type: Array,
default: null,
},
type: {
type: String,
default: 'text',
},
modelValue: {
type: [String, Number, Boolean, Array, Object],
default: '',
},
required: Boolean,
borderless: Boolean,
transparent: Boolean,
ctrlKFocus: Boolean,
name: {
type: String,
default: null,
},
id: {
type: String,
default: null,
},
autocomplete: {
type: String,
default: null,
},
placeholder: {
type: String,
default: null,
},
inputmode: {
type: String,
default: null,
},
icon: {
type: String,
default: null,
},
options: {
type: [Array, Object],
default: null,
},
type: {
type: String,
default: 'text',
},
isReadOnly: {
type: Boolean,
default: false,
},
modelValue: {
type: [String, Number, Boolean, Array, Object],
default: '',
},
required: Boolean,
borderless: Boolean,
transparent: Boolean,
ctrlKFocus: Boolean,
});
const emit = defineEmits(['update:modelValue', 'setRef']);
const computedValue = computed({
get: () => props.modelValue,
set: (value) => {
emit('update:modelValue', value);
},
get: () => props.modelValue,
set: (value) => {
emit('update:modelValue', value);
},
});
const inputElClass = computed(() => {
const base = [
'px-3 py-2 max-w-full focus:ring focus:outline-none border-gray-700 rounded w-full',
'dark:placeholder-gray-400',
computedType.value === 'textarea' ? 'h-24' : 'h-12',
props.borderless ? 'border-0' : 'border',
props.transparent ? 'bg-transparent' : 'bg-white dark:bg-slate-800',
];
if (props.icon) {
base.push('pl-10');
}
return base;
const base = [
'px-3 py-2 max-w-full focus:ring focus:outline-none border-gray-700 rounded w-full',
'dark:placeholder-gray-400',
computedType.value === 'textarea' ? 'h-24' : 'h-12',
props.borderless ? 'border-0' : 'border',
// props.transparent && !props.isReadOnly ? 'bg-transparent' : 'bg-white dark:bg-slate-800',
props.isReadOnly ? 'bg-gray-50 dark:bg-slate-600' : 'bg-white dark:bg-slate-800',
];
if (props.icon) {
base.push('pl-10');
}
return base;
});
const computedType = computed(() => (props.options ? 'select' : props.type));
const controlIconH = computed(() => (props.type === 'textarea' ? 'h-full' : 'h-12'));
@ -71,74 +76,70 @@ const selectEl = ref(null);
const textareaEl = ref(null);
const inputEl = ref(null);
onMounted(() => {
if (selectEl.value) {
emit('setRef', selectEl.value);
} else if (textareaEl.value) {
emit('setRef', textareaEl.value);
} else {
emit('setRef', inputEl.value);
}
if (selectEl.value) {
emit('setRef', selectEl.value);
} else if (textareaEl.value) {
emit('setRef', textareaEl.value);
} else {
emit('setRef', inputEl.value);
}
});
if (props.ctrlKFocus) {
const fieldFocusHook = (e) => {
if (e.ctrlKey && e.key === 'k') {
e.preventDefault();
inputEl.value.focus();
} else if (e.key === 'Escape') {
inputEl.value.blur();
}
};
onMounted(() => {
if (!mainService.isFieldFocusRegistered) {
window.addEventListener('keydown', fieldFocusHook);
mainService.isFieldFocusRegistered = true;
} else {
// console.error('Duplicate field focus event')
}
});
onBeforeUnmount(() => {
window.removeEventListener('keydown', fieldFocusHook);
mainService.isFieldFocusRegistered = false;
});
const fieldFocusHook = (e) => {
if (e.ctrlKey && e.key === 'k') {
e.preventDefault();
inputEl.value.focus();
} else if (e.key === 'Escape') {
inputEl.value.blur();
}
};
onMounted(() => {
if (!mainService.isFieldFocusRegistered) {
window.addEventListener('keydown', fieldFocusHook);
mainService.isFieldFocusRegistered = true;
} else {
// console.error('Duplicate field focus event')
}
});
onBeforeUnmount(() => {
window.removeEventListener('keydown', fieldFocusHook);
mainService.isFieldFocusRegistered = false;
});
}
</script>
<template>
<div class="relative">
<select
v-if="computedType === 'select'"
:id="id"
v-model="computedValue"
:name="name"
:class="inputElClass"
>
<option v-for="option in options" :key="option.id ?? option" :value="option">
{{ option.label ?? option }}
</option>
</select>
<textarea
v-else-if="computedType === 'textarea'"
:id="id"
v-model="computedValue"
:class="inputElClass"
:name="name"
:placeholder="placeholder"
:required="required"
/>
<input
v-else
:id="id"
ref="inputEl"
v-model="computedValue"
:name="name"
:inputmode="inputmode"
:autocomplete="autocomplete"
:required="required"
:placeholder="placeholder"
:type="computedType"
:class="inputElClass"
/>
<FormControlIcon v-if="icon" :icon="icon" :h="controlIconH" />
<slot />
</div>
<div class="relative">
<select v-if="computedType === 'select'" :id="id" v-model="computedValue" :name="name" :class="inputElClass">
<option v-if="placeholder" class="text-opacity-25" value="" disabled selected>{{ placeholder }}</option>
<option v-for="option in options" :key="option.id ?? option" :value="option.value ?? option">
{{ option.label ?? option }}
</option>
</select>
<textarea
v-else-if="computedType === 'textarea'"
:id="id"
v-model="computedValue"
:class="inputElClass"
:name="name"
:placeholder="placeholder"
:required="required"
/>
<input
v-else
:id="id"
ref="inputEl"
v-model="computedValue"
:name="name"
:inputmode="inputmode"
:autocomplete="autocomplete"
:required="required"
:placeholder="placeholder"
:type="computedType"
:class="inputElClass"
:readonly="isReadOnly"
/>
<FormControlIcon v-if="icon" :icon="icon" :h="controlIconH" />
<slot />
</div>
</template>

View file

@ -2,46 +2,47 @@
import { computed, useSlots } from 'vue';
defineProps({
label: {
type: String,
default: null,
},
labelFor: {
type: String,
default: null,
},
help: {
type: String,
default: null,
},
label: {
type: String,
default: null,
},
labelFor: {
type: String,
default: null,
},
help: {
type: String,
default: null,
},
});
const slots = useSlots();
const wrapperClass = computed(() => {
const base = [];
const slotsLength = slots.default().length;
const base = [];
const slotsLength = slots.default().length;
if (slotsLength > 1) {
base.push('grid grid-cols-1 gap-3');
}
if (slotsLength > 1) {
base.push('grid grid-cols-1 gap-3');
}
if (slotsLength === 2) {
base.push('md:grid-cols-2');
}
if (slotsLength === 2) {
base.push('md:grid-cols-2');
}
return base;
return base;
});
</script>
<template>
<div class="mb-6 last:mb-0">
<label v-if="label" :for="labelFor" class="block font-bold mb-2">{{ label }}</label>
<div v-bind:class="wrapperClass">
<slot />
</div>
<div v-if="help" class="text-xs text-gray-500 dark:text-slate-400 mt-1">
{{ help }}
</div>
</div>
<div class="mb-6 last:mb-0">
<!-- <label v-if="label" :for="labelFor" class="block font-bold mb-2">{{ label }}</label> -->
<label v-if="label" :for="labelFor" class="font-bold h-6 mt-3 text-xs leading-8 uppercase">{{ label }}</label>
<div v-bind:class="wrapperClass">
<slot />
</div>
<div v-if="help" class="text-xs text-gray-500 dark:text-slate-400 mt-1">
{{ help }}
</div>
</div>
</template>

View file

@ -0,0 +1,44 @@
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
width="100%"
height="100%"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="my-svg-component"
>
<ellipse cx="12" cy="5" rx="9" ry="3"></ellipse>
<path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"></path>
<path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"></path>
</svg>
</template>
<script lang="ts">
export default {
name: 'Icon_Mandatory',
};
</script>
<style scoped>
.my-svg-component {
/* Scoped CSS here */
width: 100%;
height: 100%;
fill: none;
stroke: currentColor;
stroke-width: 1;
stroke-linecap: round;
stroke-linejoin: round;
}
/* path,
circle {
stroke: #ffffff;
stroke-width: 6px;
fill: none;
} */
</style>

View file

@ -0,0 +1,35 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="my-svg-component">
<path
fill-rule="evenodd"
d="M9 2.25a.75.75 0 01.75.75v1.506a49.38 49.38 0 015.343.371.75.75 0 11-.186 1.489c-.66-.083-1.323-.151-1.99-.206a18.67 18.67 0 01-2.969 6.323c.317.384.65.753.998 1.107a.75.75 0 11-1.07 1.052A18.902 18.902 0 019 13.687a18.823 18.823 0 01-5.656 4.482.75.75 0 11-.688-1.333 17.323 17.323 0 005.396-4.353A18.72 18.72 0 015.89 8.598a.75.75 0 011.388-.568A17.21 17.21 0 009 11.224a17.17 17.17 0 002.391-5.165 48.038 48.038 0 00-8.298.307.75.75 0 01-.186-1.489 49.159 49.159 0 015.343-.371V3A.75.75 0 019 2.25zM15.75 9a.75.75 0 01.68.433l5.25 11.25a.75.75 0 01-1.36.634l-1.198-2.567h-6.744l-1.198 2.567a.75.75 0 01-1.36-.634l5.25-11.25A.75.75 0 0115.75 9zm-2.672 8.25h5.344l-2.672-5.726-2.672 5.726z"
clip-rule="evenodd"
/>
</svg>
</template>
<script>
export default {
name: 'Icon_Mandatory',
};
</script>
<style scoped>
.my-svg-component {
/* Scoped CSS here */
width: 100%;
height: 100%;
fill: none;
stroke: currentColor;
stroke-width: 1;
stroke-linecap: round;
stroke-linejoin: round;
}
/* path,
circle {
stroke: #ffffff;
stroke-width: 6px;
fill: none;
} */
</style>

View file

@ -0,0 +1,35 @@
<template>
<svg class="my-svg-component" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M11.42 15.17L17.25 21A2.652 2.652 0 0021 17.25l-5.877-5.877M11.42 15.17l2.496-3.03c.317-.384.74-.626 1.208-.766M11.42 15.17l-4.655 5.653a2.548 2.548 0 11-3.586-3.586l6.837-5.63m5.108-.233c.55-.164 1.163-.188 1.743-.14a4.5 4.5 0 004.486-6.336l-3.276 3.277a3.004 3.004 0 01-2.25-2.25l3.276-3.276a4.5 4.5 0 00-6.336 4.486c.091 1.076-.071 2.264-.904 2.95l-.102.085m-1.745 1.437L5.909 7.5H4.5L2.25 3.75l1.5-1.5L7.5 4.5v1.409l4.26 4.26m-1.745 1.437l1.745-1.437m6.615 8.206L15.75 15.75M4.867 19.125h.008v.008h-.008v-.008z"
/>
</svg>
</template>
<script>
export default {
name: 'Icon_Mandatory',
};
</script>
<style scoped>
.my-svg-component {
/* Scoped CSS here */
width: 100%;
height: 100%;
fill: none;
stroke: currentColor;
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round;
}
/* path,
circle {
stroke: #ffffff;
stroke-width: 6px;
fill: none;
} */
</style>

View file

@ -0,0 +1,33 @@
<template>
<svg class="my-svg-component" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path
d="M6 3a3 3 0 00-3 3v2.25a3 3 0 003 3h2.25a3 3 0 003-3V6a3 3 0 00-3-3H6zM15.75 3a3 3 0 00-3 3v2.25a3 3 0 003 3H18a3 3 0 003-3V6a3 3 0 00-3-3h-2.25zM6 12.75a3 3 0 00-3 3V18a3 3 0 003 3h2.25a3 3 0 003-3v-2.25a3 3 0 00-3-3H6zM17.625 13.5a.75.75 0 00-1.5 0v2.625H13.5a.75.75 0 000 1.5h2.625v2.625a.75.75 0 001.5 0v-2.625h2.625a.75.75 0 000-1.5h-2.625V13.5z"
/>
</svg>
</template>
<script>
export default {
name: 'Icon_Mandatory',
};
</script>
<style scoped>
.my-svg-component {
/* Scoped CSS here */
width: 100%;
height: 100%;
fill: none;
stroke: currentColor;
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round;
}
/* path,
circle {
stroke: #ffffff;
stroke-width: 6px;
fill: none;
} */
</style>

View file

@ -0,0 +1,49 @@
<template>
<div class="flex items-center relative">
<!-- v-bind:class="{ 'text-white bg-teal-600 border-teal-600': isCurrent, 'border-teal-600': isChecked }" -->
<div
class="text-gray-500 rounded-full transition duration-500 ease-in-out h-12 w-12 py-3 border-2"
:class="[
isCurrent ? 'text-white bg-teal-600 border-teal-600' : 'border-gray-300',
isChecked && 'text-teal-600 border-teal-600',
]"
>
<!-- <svg class="my-svg-component" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M11.42 15.17L17.25 21A2.652 2.652 0 0021 17.25l-5.877-5.877M11.42 15.17l2.496-3.03c.317-.384.74-.626 1.208-.766M11.42 15.17l-4.655 5.653a2.548 2.548 0 11-3.586-3.586l6.837-5.63m5.108-.233c.55-.164 1.163-.188 1.743-.14a4.5 4.5 0 004.486-6.336l-3.276 3.277a3.004 3.004 0 01-2.25-2.25l3.276-3.276a4.5 4.5 0 00-6.336 4.486c.091 1.076-.071 2.264-.904 2.95l-.102.085m-1.745 1.437L5.909 7.5H4.5L2.25 3.75l1.5-1.5L7.5 4.5v1.409l4.26 4.26m-1.745 1.437l1.745-1.437m6.615 8.206L15.75 15.75M4.867 19.125h.008v.008h-.008v-.008z"
/>
</svg> -->
<slot></slot>
<div class="absolute top-0 -ml-10 text-center mt-16 w-32 text-xs font-medium uppercase invisible sm:visible">
{{ label }}
</div>
</div>
</div>
<div v-if="!isLastStep"
class="flex-auto border-t-2 transition duration-500 ease-in-out invisible sm:visible"
:class="[isChecked ? 'border-teal-600' : 'border-gray-300']"
></div>
</template>
<script>
export default {
name: 'Icon_Multistep',
props: {
isCurrent: Boolean,
isChecked: Boolean,
isLastStep: Boolean,
label: String,
},
data() {
return {
mode: 'light',
checkedClass: 'border-teal-600',
uncheckedClass: 'border-gray-300',
};
},
};
</script>

View file

@ -7,8 +7,8 @@ import {ComputedRef} from "vue";
import { computed, ref } from 'vue';
import { containerMaxW } from '@/config.js';
// import { MainService } from '@/Stores/main.js';
import { StyleService } from '@/Stores/style.js';
import { LayoutService } from '@/Stores/layout.js';
import { StyleService } from '@/Stores/style';
import { LayoutService } from '@/Stores/layout';
import {
mdiForwardburger,
mdiBackburger,

View file

@ -1,5 +1,5 @@
<script setup>
import { StyleService } from '@/Stores/style.js'
import { StyleService } from '@/Stores/style'
// import { Link } from '@inertiajs/vue3'
import { Link } from '@inertiajs/vue3'
import { computed } from 'vue'

View file

@ -1,5 +1,5 @@
<script setup>
import { StyleService } from '@/Stores/style.js'
import { StyleService } from '@/Stores/style'
import { computed, ref, onMounted, onBeforeUnmount } from 'vue'
import { mdiChevronUp, mdiChevronDown } from '@mdi/js'
import NavBarItem from '@/Components/NavBarItem.vue'

View file

@ -1,5 +1,5 @@
<script setup>
import { StyleService } from '@/Stores/style.js';
import { StyleService } from '@/Stores/style';
defineProps({
zIndex: {

View file

@ -0,0 +1,281 @@
<template>
<!-- <input
v-model="data.search"
@change="onChange"
type="text"
class="text-base font-medium block w-full rounded-md border transition ease-in-out focus:ring-1 border-gray-300 border-solid py-2 px-3 text-gray-700 placeholder-gray-400 focus:border-blue-200 focus:ring-blue-500 focus:outline-none"
v-bind:name="props.name"
/>
<ul v-if="data.isOpen" class="mt-1 border-2 border-slate-50 overflow-auto shadow-lg rounded list-none">
<li
:class="['hover:bg-blue-100 hover:text-blue-800', 'w-full list-none text-left py-2 px-3 cursor-pointer']"
v-for="(result, i) in data.results"
:key="i"
>
{{ result.name }}
</li>
</ul> -->
<!-- <div class="flex-col justify-center relative"> -->
<div class="relative">
<input
v-model="data.search"
type="text"
:class="inputElClass"
:name="props.name"
:placeholder="placeholder"
autocomplete="off"
@keydown.down="onArrowDown"
@keydown.up="onArrowUp"
/>
<svg
class="w-4 h-4 absolute left-2.5 top-3.5"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
</svg>
</div>
<!-- <ul v-if="data.isOpen" class="bg-white border border-gray-100 w-full mt-2 max-h-28 overflow-y-auto"> -->
<!-- :ref="(el) => { ul[i] = el }" -->
<ul v-if="data.isOpen" class="bg-white dark:bg-slate-800 w-full mt-2 max-h-28 overflow-y-auto scroll-smooth">
<li
class="pl-8 pr-2 py-1 border-b-2 border-gray-100 relative cursor-pointer hover:bg-yellow-50 hover:text-gray-900"
:class="{
'bg-yellow-50 text-gray-900': i == selectedIndex,
}"
v-for="(result, i) in data.results"
:key="i"
:ref="
(el: HTMLLIElement | null) => {
ul[i] = el;
}
"
>
<svg class="absolute w-4 h-4 left-2 top-2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
<path
fill-rule="evenodd"
d="M12.293 5.293a1 1 0 011.414 0l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-2.293-2.293a1 1 0 010-1.414z"
clip-rule="evenodd"
/>
</svg>
<!-- <b>Gar</b>{{ result.name }} -->
<!-- <span>
{{ makeBold(result.name) }}
</span> -->
<span
v-for="(item, index) in makeBold(result.name)"
:key="index"
:class="{
'font-bold': data.search.toLowerCase().includes(item.toLowerCase()),
'is-active': index == selectedIndex,
}"
>
{{ item }}
</span>
</li>
</ul>
<!-- </div> -->
</template>
<script setup lang="ts">
import { reactive, ref, computed, Ref, watch } from 'vue';
import axios from 'axios';
let props = defineProps({
name: {
type: String,
required: false,
default: 'autocomplete',
},
source: {
type: [String, Array, Function],
required: true,
default: '',
},
label: {
type: String,
required: false,
default: 'name',
},
responseProperty: {
type: String,
required: false,
default: 'name',
},
placeholder: {
type: String,
default: null,
},
icon: {
type: String,
default: null,
},
required: Boolean,
borderless: Boolean,
transparent: Boolean,
ctrlKFocus: Boolean,
});
const inputElClass = computed(() => {
const base = [
'px-3 py-2 max-w-full focus:ring focus:outline-none border-gray-700 rounded w-full',
'dark:placeholder-gray-400',
'h-12',
props.borderless ? 'border-0' : 'border',
props.transparent ? 'bg-transparent' : 'bg-white dark:bg-slate-800',
// props.isReadOnly ? 'bg-gray-50 dark:bg-slate-600' : 'bg-white dark:bg-slate-800',
];
// if (props.icon) {
base.push('pl-10');
// }
return base;
});
let search = ref('');
let data = reactive({
search: search,
isOpen: false,
results: [],
});
let error = ref('');
let selectedIndex: Ref<number> = ref(0);
// const listItem = ref(null);
const ul: Ref<Array<HTMLLIElement | null>> = ref([]);
watch(selectedIndex, (selectedIndex) => {
if (selectedIndex != null && ul.value != null) {
const currentElement: HTMLLIElement | null = ul.value[selectedIndex];
currentElement &&
currentElement.scrollIntoView({
behavior: 'smooth',
block: 'nearest',
inline: 'start',
});
}
});
watch(search, async () => {
await onChange();
});
// function clear() {
// data.search = "";
// data.isOpen = false;
// data.results = [];
// error.value = "";
// // this.$emit("clear");
// }
// function onChange() {
// if (!props.source || !data.search) {
// return false;
// }
// data.isOpen = true;
// arrayLikeSearch(props.source);
// }
async function onChange() {
if (!props.source || !data.search) return false;
selectedIndex.value = 0;
if (data.search.length >= 2) {
data.isOpen = true;
switch (true) {
case typeof props.source === 'string':
return await request(props.source, data.search);
// case typeof props.source === 'function':
// return props.source(data.search).then((response) => {
// data.results = getResults(response);
// });
case Array.isArray(props.source):
return arrayLikeSearch(props.source);
default:
throw new Error('typeof source is ' + typeof props.source);
}
} else {
data.results = [];
data.isOpen = false;
}
}
function getResults(response) {
// if (props.responseProperty) {
// let foundObj;
// JSON.stringify(response, (_, nestedValue) => {
// if (nestedValue && nestedValue[props.responseProperty]) foundObj = nestedValue[props.responseProperty];
// return nestedValue;
// });
// return foundObj;
// }
if (Array.isArray(response)) {
return response;
}
return [];
}
// function setResult(result) {
// data.search = result[props.label];
// data.isOpen = false;
// }
// function request(url) {
// return axios.get(url).then((response) => {
// data.results = getResults(response);
// });
// }
async function request(url, param) {
try {
let response = await searchTerm(url, param);
error.value = '';
data.results = getResults(response);
// this.results = res.data;
// this.loading = false;
} catch (error) {
error.value = error.message;
// this.loading = false;
}
}
async function searchTerm(term: string, param): Promise<any> {
let res = await axios.get(term, { params: { filter: param } });
// console.log(res.data);
return res.data; //.response;//.docs;
}
// async function request(term: string): Promise<any> {
// let res = await axios.get('/api/persons', { params: { filter: term } });
// return res.data; //.response;//.docs;
// }
function arrayLikeSearch(items) {
data.results = items.filter((item) => {
return item.toLowerCase().indexOf(data.search.toLowerCase()) > -1;
});
}
function makeBold(suggestion) {
const query = data.search.valueOf();
const regex = new RegExp(query.split('').join('-?'), 'i');
const test = suggestion.replace(regex, (match) => '<split>' + match + '<split>');
// return suggestion.match(regex);
// const splitWord = suggestion.match(regex);
return test.split('<split>');
}
function onArrowDown() {
if (data.results.length > 0) {
selectedIndex.value = selectedIndex.value === data.results.length - 1 ? 0 : selectedIndex.value + 1;
// const currentElement: HTMLLIElement = ul.value[selectedIndex.value];
}
}
function onArrowUp() {
if (data.results.length > 0) {
selectedIndex.value = selectedIndex.value == 0 || selectedIndex.value == -1 ? data.results.length - 1 : selectedIndex.value - 1;
}
}
</script>

View file

@ -1,9 +1,9 @@
<script setup>
import { containerMaxW } from '@/config.js'
import { containerMaxW } from '@/config.js';
</script>
<template>
<section class="p-6" v-bind:class="containerMaxW">
<slot />
</section>
<section class="p-6" v-bind:class="containerMaxW">
<slot />
</section>
</template>