POC builder done

This commit is contained in:
Jordan Blasenhauer 2024-05-17 11:35:09 +02:00
parent 93836285f0
commit fca7cc5d02
13 changed files with 367 additions and 81 deletions

File diff suppressed because one or more lines are too long

View file

@ -1,33 +1,59 @@
<script setup>
import { reactive, onBeforeMount } from "vue";
import Checkbox from "@components/Forms/Field/Checkbox.vue";
import Select from "@components/Forms/Field/Select.vue";
import Input from "@components/Forms/Field/Input.vue";
import GridLayout from "@components/Widget/GridLayout.vue";
import Grid from "@components/Widget/Grid.vue";
const props = defineProps({
builder : {
type: Array,
required: true,
},
})
// Example of builder
const builder = [{
// we are starting with the top level container name
// this can be a "card", "modal", "table"... etc
"type": "card",
// a card can have an icon and a color at the top right
"icon": ["iconName", "iconColor"],
// grid position for each screen
"columns" : [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
"containerClass": "", // tailwind css grid class (items-start, ...)
"containerColumns" : {"pc": 12, "tablet": 12, "mobile": 12},
// container title
"title" : "My awesome card",
// group of widgets will share same columns
// Case we want a button widget in a one column group and others in like a 3 columns group
content: [
{
position="left",
columns={
pc=4,
tablet=12,
..
},
widgets=[Input1, Button2, { type="input", data = {...} }]
},
...
]
// Each widget need a name (here type) and associated data
// We need to send specific data for each widget type
widgets: [
{
type : "Checkbox",
data : {containerClass : "", columns : {"pc": 6, "tablet": 12, "mobile": 12}, id:"test-check", value: "yes", label: "Checkbox", name: "checkbox", required: true, version: "v1.0.0", hideLabel: false, headerClass: "text-red-500" }
}, {
type : "Select",
data : {containerClass : "", columns : {"pc": 6, "tablet": 12, "mobile": 12}, id: 'test-select', value: 'yes', values: ['yes', 'no'], name: 'test-select', disabled: false, required: true, label: 'Test select', tabId: '1',}
}
]
},
// ... other containers
]
</script>
<template>
<div class="grid grid-cols-12">
<!-- top level grid (layout) -->
<GridLayout v-for="(container, index) in props.builder" :key="index"
:gridLayoutClass="container.containerClass"
:type="container.type"
:title="container.title"
:columns="container.containerColumns">
<!-- widget grid -->
<Grid>
<!-- widget element -->
<template v-for="(widget, index) in container.widgets" :key="index">
<Checkbox v-if="widget.type === 'Checkbox'" v-bind="widget.data"></Checkbox>
<Select v-if="widget.type === 'Select'" v-bind="widget.data"></Select>
<Input v-if="widget.type === 'Input'" v-bind="widget.data"></Input>
</template>
</Grid>
</GridLayout>
</div>
</template>

View file

@ -1,9 +0,0 @@
<script setup>
</script>
<template>
<div class="m-1 p-1">
<slot></slot>
</div>
</template>

View file

@ -1,13 +1,18 @@
<script setup>
import { reactive, defineProps, onMounted, ref } from "vue";
import { contentIndex } from "@utils/tabindex.js";
import Base from "@components/Forms/Field/Base.vue";
import Container from "@components/Widget/Container.vue";
import Header from "@components/Forms/Header/Field.vue";
/* PROPS ARGUMENTS
/*
This checkbox component is used to create a complete checkbox (label, validator message).
It is mainly use for checkbox setting form.
PROPS ARGUMENTS
*
*
id: string,
columns: object,
value: string,
disabled: boolean,
required: boolean,
@ -16,18 +21,25 @@ import Header from "@components/Forms/Header/Field.vue";
version: string,
hideLabel: boolean,
required: boolean,
containerClass: string,
headerClass: string,
inpClass: string,
tabId: string || number,
*
*
*/
const props = defineProps({
// id && value && method
id: {
type: String,
required: true,
},
columns: {
type: [Object, Boolean],
required: false,
default : false
},
value: {
type: String,
required: true,
@ -56,6 +68,10 @@ const props = defineProps({
type: Boolean,
required: false,
},
containerClass : {
type: String,
required: false
},
headerClass: {
type: String,
required: false,
@ -91,7 +107,7 @@ onMounted(() => {
</script>
<template>
<Base>
<Container :containerClass="`w-full m-1 p-1 ${props.containerClass}`" :columns="props.columns">
<Header :required="props.required" :name="props.name" :label="props.label" :hideLabel="props.hideLabel" :headerClass="props.headerClass" />
<div class="relative z-10 flex flex-col items-start">
@ -141,5 +157,5 @@ onMounted(() => {
}}
</p>
</div>
</Base>
</Container>
</template>

View file

@ -1,14 +1,19 @@
<script setup>
import { reactive, ref, defineEmits, onMounted, defineProps } from "vue";
import { contentIndex } from "@utils/tabindex.js";
import Base from "@components/Forms/Field/Base.vue";
import Container from "@components/Widget/Container.vue";
import Header from "@components/Forms/Header/Field.vue";
/* PROPS ARGUMENTS
/*
This input component is used to create a complete input (label, validator message).
It is mainly use for input setting form.
PROPS ARGUMENTS
*
*
id: string,
columns : <object|boolean>,
name: string,
type: string<"text"|"email"|"password"|"number"|"tel"|"url">,
disabled: boolean,
@ -22,6 +27,7 @@ import Header from "@components/Forms/Header/Field.vue";
version: string,
hideLabel: boolean,
required: boolean,
containerClass: string,
headerClass: string,
inpClass: string,
tabId: string || number,
@ -34,6 +40,11 @@ const props = defineProps({
type: String,
required: true,
},
columns : {
type : [Object, Boolean],
required: false,
default : false
},
name: {
type: String,
required: true,
@ -82,6 +93,10 @@ const props = defineProps({
type: Boolean,
required: false,
},
containerClass : {
type: String,
required: false
},
headerClass: {
type: String,
required: false,
@ -141,7 +156,7 @@ onMounted(() => {
</script>
<template>
<Base>
<Container :containerClass="`w-full m-1 p-1 ${props.containerClass}`" :columns="props.columns">
<Header :required="props.required" :name="props.name" :label="props.label" :hideLabel="props.hideLabel" :headerClass="props.headerClass" />
<div class="relative flex flex-col items-start">
@ -259,5 +274,5 @@ onMounted(() => {
}}
</p>
</div>
</Base>
</Container>
</template>

View file

@ -1,14 +1,19 @@
<script setup>
import { ref, reactive, watch, onMounted, defineEmits, defineProps } from "vue";
import { contentIndex } from "@utils/tabindex.js";
import Base from "@components/Forms/Field/Base.vue";
import Container from "@components/Widget/Container.vue";
import Header from "@components/Forms/Header/Field.vue";
/* PROPS ARGUMENTS
/*
This select component is used to create a complete select (label, validator message).
It is mainly use for select setting form.
PROPS ARGUMENTS
*
*
id: string,
columns : <object|boolean>,
value: string,
values: array,
disabled: boolean,
@ -17,6 +22,7 @@ import Header from "@components/Forms/Header/Field.vue";
name: string,
version: string,
hideLabel: boolean,
containerClass: string,
inpClass: string,
headerClass: string,
tabId: string || number,
@ -31,6 +37,11 @@ const props = defineProps({
type: String,
required: true,
},
columns: {
type: [Object, Boolean],
required: false,
default: false
},
value: {
type: String,
required: true,
@ -63,6 +74,10 @@ const props = defineProps({
type: Boolean,
required: false,
},
containerClass : {
type: String,
required: false
},
headerClass: {
type: String,
required: false,
@ -150,7 +165,7 @@ const emits = defineEmits(["inp"]);
</script>
<template>
<Base>
<Container :containerClass="`w-full m-1 p-1 ${props.containerClass}`" :columns="props.columns">
<Header :required="props.required" :name="props.name" :label="props.label" :hideLabel="props.hideLabel" :headerClass="props.headerClass" />
<select :name="props.name" class="hidden">
@ -227,6 +242,6 @@ const emits = defineEmits(["inp"]);
<!-- end dropdown-->
</div>
<!-- end custom-->
</Base>
</Container>
</template>

View file

@ -1,7 +1,13 @@
<script setup>
import { defineProps } from "vue";
/* PROPS ARGUMENTS
/*
This header component is used with field in order to link a label to field type.
We can show additionnal data, like validaiton pattern with invalid message,
Or add some popovers elements.
PROPS ARGUMENTS
*
*
label: string,

View file

@ -0,0 +1,40 @@
<script setup>
import { computed } from 'vue';
/*
This container component is mainly use as widget container.
Each widget can define the base class for the container.
In case we columns, this will use it too for positioning.
PROPS ARGUMENTS
*
*
class: string,
columns: object,
*
*
*/
const props = defineProps({
containerClass : {
type: String,
required: false,
default : ""
},
columns: {
type: [Object, Boolean],
required: false,
default: false
},
})
const gridClass = computed(() => {
return props.columns ? `col-span-${props.columns.mobile} md:col-span-${props.columns.tablet} lg:col-span-${props.columns.pc}` : '';
})
</script>
<template>
<div :class="[props.containerClass, gridClass]">
<slot></slot>
</div>
</template>

View file

@ -0,0 +1,34 @@
<script setup>
import { computed } from 'vue';
/*
This container component is used to align groups of components horizontally using flex.
PROPS ARGUMENTS
*
*
class: string,
horizontalAlign: string<"flex-start"|"center"|"flex-end"|"space-between"|"space-around"|"space-evenly">,
*
*
*/
const props = defineProps({
class: {
type: String,
required: false,
default : "flex-start"
},
})
const flexClass = computed(() => {
return `w-full flex ${props.class}`;
})
</script>
<template>
<div :class="[flexClass]">
<slot></slot>
</div>
</template>

View file

@ -0,0 +1,50 @@
<script setup>
import { computed } from 'vue';
/*
This Grid component is a container with a grid system.
In case we are adding columns, this will be added, so it can be used with parent grid.
PROPS ARGUMENTS
*
*
type : <"card"|"table"|...> (will determine component style)
title: string,
columns : { pc: int, tablet: int, mobile: int},
class : <"items-start"|"items-center"|"items-end">
*
*
*/
const props = defineProps({
columns : {
type: [Object, Boolean],
required: false,
default : {
pc: 12,
tablet: 12,
mobile: 12}
},
gridClass : {
type: String,
required: false,
default: "items-start"
},
})
const gridClass = computed(() => {
return `grid grid-cols-12 w-full ${props.gridClass}`;
})
const columnClass = computed(() => {
return props.columns ? `col-span-${props.columns.mobile} md:col-span-${props.columns.tablet} lg:col-span-${props.columns.pc}` : ``;
})
</script>
<template>
<div :class="[gridClass, columnClass]">
<slot></slot>
</div>
</template>

View file

@ -0,0 +1,68 @@
<script setup>
import { computed } from 'vue';
/*
This GridLayout component is used at the top level of a page layout.
This component will determine the position of layout components based on the grid columns.
We can create card, modal, table and others top level layout using this component.
The content of this component is grid based.
PROPS ARGUMENTS
*
*
type : <"card"|"table"|...> (will determine component style)
title: string,
columns : { pc: int, tablet: int, mobile: int},
verticalAlign : <"items-start"|"items-center"|"items-end">
*
*
*/
const props = defineProps({
type : {
type: String,
required: false,
default : "grid"
},
title : {
type: String,
required: false,
default : ""
},
columns : {
type: Object,
required: false,
default : {
pc: 12,
tablet: 12,
mobile: 12}
},
gridLayoutClass : {
type: String,
required: false,
default: "items-start"
},
})
const containerClass = computed(() => {
if(props.type === 'card') return 'bg-white rounded shadow-md w-full';
return '';
})
const gridClass = computed(() => {
return `grid grid-cols-12 w-full col-span-${props.columns.mobile} md:col-span-${props.columns.tablet} lg:col-span-${props.columns.pc}`;
})
const titleClass = computed(() => {
if(props.type === 'card') return 'text-2xl font-bold text-center m-4';
return ''
})
</script>
<template>
<div :class="[containerClass, gridClass, props.gridLayoutClass]">
<h1 v-if="props.title" :class="[titleClass]">{{ props.title }}</h1>
<slot></slot>
</div>
</template>

View file

@ -3,7 +3,7 @@ import { reactive, onBeforeMount } from "vue";
import Checkbox from "@components/Forms/Field/Checkbox.vue";
import Select from "@components/Forms/Field/Select.vue";
import Input from "@components/Forms/Field/Input.vue";
import Builder from "@components/Builder.vue";
// Define reactive properties
const data = reactive({
// Define properties here
@ -16,48 +16,34 @@ onBeforeMount(() => {
data.title = dataEl.getAttribute('data-flask') && dataEl.getAttribute('data-flask') !== "{{ flask_data }}" ? dataEl.getAttribute('data-flask') : dataEl.getAttribute('data-default-value');
})
const checkboxData = {
id: 'test-checkbox',
value: 'yes',
name: 'test-checkbox',
disabled: false,
required: true,
label: 'Test checkbox',
tabId: '1',
}
const selectData = {
id: 'test-select',
value: 'yes',
values: ['yes', 'no'],
name: 'test-select',
disabled: false,
required: true,
label: 'Test select',
tabId: '1',
}
const inputData = {
id: 'test-input',
value: 'yes',
type: "text",
name: 'test-input',
disabled: false,
required: true,
label: 'Test input',
pattern : "(test)",
tabId: '1',
}
// Example of builder
const builder = [{
// we are starting with the top level container name
// this can be a "card", "modal", "table"... etc
"type": "card",
"containerClass": "", // tailwind css grid class (items-start, ...)
"containerColumns" : {"pc": 12, "tablet": 12, "mobile": 12},
// container title
"title" : "My awesome card",
// Each widget need a name (here type) and associated data
// We need to send specific data for each widget type
widgets: [
{
type : "Checkbox",
data : {containerClass : "", columns : {"pc": 6, "tablet": 12, "mobile": 12}, id:"test-check", value: "yes", label: "Checkbox", name: "checkbox", required: true, version: "v1.0.0", hideLabel: false, headerClass: "text-red-500" }
}, {
type : "Select",
data : {containerClass : "", columns : {"pc": 6, "tablet": 12, "mobile": 12}, id: 'test-select', value: 'yes', values: ['yes', 'no'], name: 'test-select', disabled: false, required: true, label: 'Test select', tabId: '1',}
}
]
},
]
</script>
<template>
<div class="bg-secondary flex flex-col items-center justify-center h-full">
<div style="width: 300px;">
<Checkbox v-bind="checkboxData" />
<Select v-bind="selectData" />
<Input v-bind="inputData" />
<Builder :builder="builder" />
</div>
</div>
</template>

View file

@ -7,6 +7,45 @@ export default {
"./src/components/**/*.{js,vue,ts,jsx,tsx,mdx}",
"../setup/src/*.{js,vue,ts,jsx,tsx,mdx}",
],
safelist : [
"col-span-1",
"col-span-2",
"col-span-3",
"col-span-4",
"col-span-5",
"col-span-6",
"col-span-7",
"col-span-8",
"col-span-9",
"col-span-10",
"col-span-11",
"col-span-12",
"md:col-span-1",
"md:col-span-2",
"md:col-span-3",
"md:col-span-4",
"md:col-span-5",
"md:col-span-6",
"md:col-span-7",
"md:col-span-8",
"md:col-span-9",
"md:col-span-10",
"md:col-span-11",
"md:col-span-12",
"lg:col-span-1",
"lg:col-span-2",
"lg:col-span-3",
"lg:col-span-4",
"lg:col-span-5",
"lg:col-span-6",
"lg:col-span-7",
"lg:col-span-8",
"lg:col-span-9",
"lg:col-span-10",
"lg:col-span-11",
"lg:col-span-12",
],
important: true,
darkMode: "class",
theme: {