Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
allanmcarvalho committed Sep 13, 2023
1 parent d31bb06 commit c569b2d
Show file tree
Hide file tree
Showing 9 changed files with 279 additions and 56 deletions.
36 changes: 32 additions & 4 deletions resources/js/Components/Form/FillableInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
<input
:type="type"
:id="id"
maxlength="100"
v-model="form[field]"
class="form-input block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-primary-600 sm:text-sm sm:leading-6" />
placeholder="Placeholder"
:class="['lvw-input', hasError ? 'lvw-red' : `lvw-${theme}`]" />
</template>

<script lang="ts">
Expand All @@ -16,17 +18,43 @@ export default defineComponent({
id: String,
field: {type: String, required: true},
form: {type: Object as PropType<InertiaForm<object>>, required: true},
theme: String,
type: {
type: String,
default: 'text',
validator(value: string): boolean {
return FillableTypes.includes(value);
}
}
}
},
computed: {
hasError(): boolean {
return !!this.form.errors[this.field];
}
},
});
</script>

<style scoped>
<style lang="scss" scoped>
.lvw-input {
@apply form-input block w-full rounded-md border-0 py-1.5 shadow-sm ring-1 ring-inset focus:ring-2 focus:ring-inset sm:text-sm sm:leading-6;
&.lvw-gray {
@apply text-gray-800 ring-gray-500 placeholder:text-gray-800/60 focus:ring-gray-600
}
&.lvw-green {
@apply text-green-900 ring-green-500 placeholder:text-green-800/60 focus:ring-green-600
}
&.lvw-indigo {
@apply text-indigo-900 ring-indigo-500 placeholder:text-indigo-800/60 focus:ring-indigo-600
}
&.lvw-primary {
@apply text-primary-900 ring-primary-500 placeholder:text-primary-800/60 focus:ring-primary-600
}
&.lvw-slate {
@apply text-slate-900 ring-slate-500 placeholder:text-slate-800/60 focus:ring-slate-600
}
&.lvw-red {
@apply text-red-700 ring-red-500 placeholder:text-red-700/60 focus:ring-red-600
}
}
</style>
76 changes: 76 additions & 0 deletions resources/js/Components/Form/InputFeedback.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<template>
<div :class="['lvw-input-feedback', {'lvw-without-feedback': !hasFeedback}]">
<div v-if="hasFeedback" :class="['lvw-feedback', errors !== false ? 'lvw-red' : `lvw-${theme}`]" @click="focusInput">
{{ finalMessage }}
</div>
<MaxLength v-if="showMaxLength" :id="id" :field="field" :form="form" :show-max-length="showMaxLength" :theme="theme"/>
</div>
</template>

<script lang="ts">
import {defineComponent, PropType} from "vue";
import {InertiaForm} from "@inertiajs/vue3/types/useForm";
import MaxLength from "./MaxLength.vue";
export default defineComponent({
name: "InputFeedback",
components: {MaxLength},
props: {
id: String,
feedback: String,
field: {type: String, required: true},
form: {type: Object as PropType<InertiaForm<object>>, required: true},
showMaxLength: Boolean,
theme: String,
},
computed: {
errors(): string|false
{
let errors: undefined|string|false = this.form.errors[this.field];
return errors !== false && (errors === undefined || errors.trim() === '') ? false : errors;
},
finalMessage(): false|string {
let feedback : string|undefined|false = this.feedback;
return feedback === undefined || feedback.trim() === '' ? false : feedback;
},
hasFeedback(): boolean {
return this.finalMessage !== false;
},
},
methods: {
focusInput() {
document.getElementById(this.id)?.focus();
},
},
})
</script>

<style lang="scss" scoped>
.lvw-input-feedback {
@apply mt-1 w-full flex justify-between items-start gap-2;
&.lvw-without-feedback {
@apply flex-row-reverse;
}
.lvw-feedback {
@apply grow text-sm ps-1;
&.lvw-gray {
@apply text-gray-800/80
}
&.lvw-green {
@apply text-green-900/80
}
&.lvw-indigo {
@apply text-indigo-900/80
}
&.lvw-primary {
@apply text-primary-900/80
}
&.lvw-slate {
@apply text-slate-900/80
}
&.lvw-red {
@apply text-red-700/80
}
}
}
</style>
32 changes: 24 additions & 8 deletions resources/js/Components/Form/InputGroup.vue
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
<template>
<div v-bind="parentAttrs">
<slot v-if="labelSlotExists" :id="finalId" :field="field" :form="form" :help="help" :label="label" :required="required" name="label"/>
<InputLabel v-else-if="!!label" :id="finalId" :field="field" :form="form" :help="help" :required="required">{{ label }}</InputLabel>
<div class="mt-2">
<slot v-if="inputSlotExists" :id="finalId" :field="field" :form="form" :type="type" name="input"/>
<FillableInput v-bind="$attrs" v-else v-model="form[field]" :id="finalId" :field="field" :form="form" :type="type"/>
<slot v-if="labelSlotExists" :id="finalId" :field="field" :form="form" :help="help" :label="label" :required="required" :theme="theme" name="label"/>
<InputLabel v-else-if="!!label" :id="finalId" :field="field" :form="form" :help="help" :required="required" :theme="theme">{{ label }}</InputLabel>
<div class="mt-1">
<slot v-if="inputSlotExists" :id="finalId" :field="field" :form="form" :theme="theme" :type="type" name="input"/>
<FillableInput v-bind="$attrs" v-else v-model="form[field]" :id="finalId" :field="field" :form="form" :theme="theme" :type="type"/>
</div>
<InputHelp/>
<slot v-if="feedbackSlotExists" :id="finalId" :field="field" :form="form" :showMaxLength="showMaxLength" :theme="theme" name="feedback"/>
<InputFeedback v-else :id="finalId" :feedback="feedback" :field="field" :form="form" :show-max-length="showMaxLength" :theme="theme"/>
</div>
</template>

<script lang="ts">
import FillableInput from "./FillableInput.vue";
import InputLabel from "./InputLabel.vue";
import InputHelp from "./InputHelp.vue";
import InputFeedback from "./InputFeedback.vue";
import type { PropType } from 'vue'
import {defineComponent} from "vue";
import {InertiaForm} from "@inertiajs/vue3/types/useForm";
Expand All @@ -23,23 +24,38 @@ export default defineComponent({
name: "InputGroup",
components: {
FillableInput,
InputHelp,
InputFeedback,
InputLabel
},
props: {
id: String,
feedback: String,
field: {type: String, required: true},
form: {type: Object as PropType<InertiaForm<object>>, required: true},
help: String,
label: String,
parentAttrs: Object,
required: Boolean,
showMaxLength: {
type: Boolean,
default: true,
},
theme: {
type: String,
default: 'primary',
validator(value: string): boolean {
return ['gray', 'green', 'indigo', 'primary', 'slate'].includes(value);
}
},
type: String,
},
computed: {
finalId(): string {
return this.id || 'input-' + Math.random().toString(16).slice(2);
},
feedbackSlotExists(): boolean {
return !!this.$slots.feedback;
},
inputSlotExists(): boolean {
return !!this.$slots.label;
},
Expand Down
15 changes: 0 additions & 15 deletions resources/js/Components/Form/InputHelp.vue

This file was deleted.

34 changes: 28 additions & 6 deletions resources/js/Components/Form/InputLabel.vue
Original file line number Diff line number Diff line change
@@ -1,26 +1,28 @@
<template>
<label :for="id" :class="[hasError ? 'text-red-600' : 'text-gray-900', 'flex items-center gap-1 text-sm font-medium leading-6']">
<label :for="id" :class="['lvw-input-label', hasError ? 'lvw-red' : `lvw-${theme}`]">
<slot/>
<span class="text-red-600" v-if="required">*</span>
<Question v-if="help" v-tooltip="help" class="inline w-3.5 h-3.5 bg-sky-600 rounded-full p-[0.2em] fill-white"/>
<Asterisk v-if="required" class="inline w-2 h-2 fill-red-600"/>
<Question v-if="help" v-tooltip="help" class="inline w-3.5 h-3.5 fill-sky-700"/>
</label>
</template>

<script lang="ts">
import {defineComponent, PropType} from "vue";
import Tooltip from "../../Directives/Tooltip";
import {InertiaForm} from "@inertiajs/vue3/types/useForm";
import Asterisk from "../Icons/Asterisk.vue";
import Question from "../Icons/Question.vue";
export default defineComponent({
name: "InputLabel",
components: {Question},
components: {Asterisk, Question},
props: {
id: String,
field: {type: String, required: true},
form: {type: Object as PropType<InertiaForm<object>>, required: true},
help: String,
required: Boolean,
theme: String,
},
directives: {
Tooltip,
Expand All @@ -33,6 +35,26 @@ export default defineComponent({
});
</script>

<style scoped>
<style lang="scss" scoped>
.lvw-input-label {
@apply flex items-center gap-1 text-sm font-medium leading-6;
&.lvw-gray {
@apply text-gray-800
}
&.lvw-green {
@apply text-green-900
}
&.lvw-indigo {
@apply text-indigo-900
}
&.lvw-primary {
@apply text-primary-900
}
&.lvw-slate {
@apply text-slate-900
}
&.lvw-red {
@apply text-red-700
}
}
</style>
109 changes: 109 additions & 0 deletions resources/js/Components/Form/MaxLength.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<template>
<div v-if="maxlength !== null && length !== null && showMaxLength" :class="['lvw-input-maxlength', `lvw-${theme}`]">
{{ `${fLength}/${fMaxlength}` }}
</div>
</template>

<script lang="ts">
import {defineComponent, PropType} from "vue";
import {InertiaForm} from "@inertiajs/vue3/types/useForm";
export default defineComponent({
name: "MaxLength",
props: {
id: String,
field: {type: String, required: true},
form: {type: Object as PropType<InertiaForm<object>>, required: true},
showMaxLength: Boolean,
theme: String,
},
data() {
return {
el: null,
maxlength: null,
length: null,
}
},
computed: {
fLength(): string {
return Intl.NumberFormat().format(this.length);
},
fMaxlength(): string {
return Intl.NumberFormat().format(this.maxlength);
},
limit(): number|null {
if (this.maxlength === null) {
return null;
}
let constant = (1 - (this.maxlength > 1000 ? 100 : this.maxlength / 10) / 100) / 2;
let limit = Math.floor(this.maxlength * constant);
let halfMaxlength = this.maxlength / 2;
if (limit > halfMaxlength) {
limit = Math.floor(this.maxlength / 2);
}
if (this.maxlength > 250) {
limit = 30;
}
return limit;
},
// attentionLimit(): number|null {
// return this.length >= this.maxlength;
// },
// warnLimit(): number|null {
// return this.length >= this.maxlength;
// },
},
mounted() {
this.init();
this.getLength(this.form[this.field]);
this.$watch('form.' + this.field, this.getLength);
console.log(this.limit);
},
methods: {
getLength(word): null | number {
this.length = word?.length ?? 0;
},
getMaxlength(): null | number {
let maxlength: string | null = this?.el.getAttribute('maxlength');
return typeof maxlength === 'string' && /^\d+$/.test(maxlength) ? parseInt(maxlength) : null;
},
init(): void {
this.el = document.getElementById(this.id);
this.maxlength = this.getMaxlength();
}
},
});
</script>

<style lang="scss" scoped>
.lvw-input-maxlength {
@apply inline-flex items-center rounded-lg mt-[0.15em] px-[0.3em] py-[0.05em] text-[0.7em] font-medium ;
&.lvw-gray {
@apply bg-gray-600/30 text-gray-700
}
&.lvw-green {
@apply bg-green-600/30 text-green-700
}
&.lvw-indigo {
@apply bg-indigo-600/30 text-indigo-700
}
&.lvw-primary {
@apply bg-primary-600/30 text-primary-700
}
&.lvw-slate {
@apply bg-slate-600/30 text-slate-700
}
&.lvw-red {
@apply bg-red-600/30 text-red-700
}
&.lvw-yellow {
@apply bg-yellow-600/30 text-yellow-700
}
}
</style>
Loading

0 comments on commit c569b2d

Please sign in to comment.