mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
POC builder done
This commit is contained in:
parent
93836285f0
commit
fca7cc5d02
13 changed files with 367 additions and 81 deletions
File diff suppressed because one or more lines are too long
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
<script setup>
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="m-1 p-1">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
40
vuejs/client/src/components/Widget/Container.vue
Normal file
40
vuejs/client/src/components/Widget/Container.vue
Normal 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>
|
||||
34
vuejs/client/src/components/Widget/Flex.vue
Normal file
34
vuejs/client/src/components/Widget/Flex.vue
Normal 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>
|
||||
50
vuejs/client/src/components/Widget/Grid.vue
Normal file
50
vuejs/client/src/components/Widget/Grid.vue
Normal 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>
|
||||
68
vuejs/client/src/components/Widget/GridLayout.vue
Normal file
68
vuejs/client/src/components/Widget/GridLayout.vue
Normal 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>
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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: {
|
||||
|
|
|
|||
Loading…
Reference in a new issue