enhance details style + start filtering

This commit is contained in:
Jordan Blasenhauer 2024-06-20 14:13:44 +02:00
parent 5b8a60488b
commit 8f92d8a197
14 changed files with 211 additions and 105 deletions

View file

@ -32,6 +32,10 @@
text-transform: uppercase;
}
.break-word {
word-break: break-word;
}
/* LOGIN */
.login-main {
@ -486,6 +490,16 @@ body {
@apply flex scale-110 h-5 w-5 items-center align-middle;
}
/* LAYOUT */
.layout-grid-layout {
@apply px-6 py-4 overflow-hidden break-words grid grid-cols-12 w-full;
}
.layout-grid {
@apply col-span-12 grid grid-cols-12 w-full relative;
}
/* POPOVER */
@ -991,11 +1005,11 @@ body {
/* CONTENT COMPONENT */
.text-content {
@apply leading-normal mb-0 ;
@apply leading-normal mb-0 break-word;
}
.text-stat {
@apply my-1 font-bold dark:text-white/90 text-black uppercase;
@apply my-1 font-bold dark:text-white/90 text-black uppercase break-word;
}
/* LIST COMPONENT */
@ -1016,42 +1030,41 @@ body {
}
.list-details-container {
@apply grid grid-cols-12 gap-2 my-4;
@apply col-span-12 grid grid-cols-12 gap-2 my-4;
}
.list-details {
@apply py-3 min-h-12 relative col-span-12 sm:col-span-6 2xl:col-span-4 3xl:col-span-3 p-1 flex justify-between items-center transition rounded;
.list-details-item {
@apply z-0 px-3 py-2 m-1 min-h-12 relative flex justify-between items-center transition rounded;
}
.enabled.list-details {
@apply bg-gray-100 hover:bg-gray-300 dark:bg-slate-700 dark:hover:bg-slate-800;
.enabled.list-details-item {
@apply bg-gray-100 hover:bg-gray-200 dark:bg-slate-700 dark:hover:bg-slate-800;
}
.disabled.list-details {
.disabled.list-details-item {
@apply cursor-not-allowed bg-gray-300 dark:bg-gray-800;
}
/* TITLE */
.title-container {
@apply capitalize-first w-full max-w-[80%] sm:max-w-[800px] col-span-12 break-words font-bold dark:text-white/90 transition duration-300 ease-in-out text-[1.3rem] text-[#344767] tracking-normal;
@apply capitalize-first break-word w-full max-w-[80%] sm:max-w-[800px] col-span-12 font-bold dark:text-white/90 transition duration-300 ease-in-out text-[1.3rem] text-[#344767] tracking-normal;
}
.title-card {
@apply capitalize-first w-full max-w-[80%] sm:max-w-[700px] col-span-12 break-words font-bold dark:text-white/90 transition duration-300 ease-in-out text-xl text-[#344767] tracking-normal;
@apply capitalize-first break-word w-full max-w-[80%] sm:max-w-[700px] col-span-12 font-bold dark:text-white/90 transition duration-300 ease-in-out text-xl text-[#344767] tracking-normal;
}
.title-content {
@apply capitalize-first w-full max-w-[80%] sm:max-w-[700px] col-span-12 break-words font-bold dark:text-white/90 transition duration-300 ease-in-out text-lg text-[#344767];
@apply capitalize-first break-word w-full max-w-[80%] sm:max-w-[700px] col-span-12 font-bold dark:text-white/90 transition duration-300 ease-in-out text-lg text-[#344767];
}
.title-min {
@apply capitalize-first w-full max-w-[80%] sm:max-w-[700px] col-span-12 break-words font-bold dark:text-white/90 transition duration-300 ease-in-out text-[0.95rem] text-[#344767];
@apply capitalize-first break-word w-full max-w-[80%] sm:max-w-[700px] col-span-12 font-bold dark:text-white/90 transition duration-300 ease-in-out text-[0.95rem] text-[#344767];
}
.title-stat {
@apply capitalize-first w-full max-w-[80%] sm:max-w-[600px] col-span-12 font-sans text-sm font-semibold leading-normal uppercase dark:text-white/90 text-[#344767];
@apply capitalize-first break-word w-full max-w-[80%] sm:max-w-[600px] col-span-12 font-sans text-sm font-semibold leading-normal uppercase dark:text-white/90 text-[#344767];
}
.no-subtitle.title-container {
@ -1082,23 +1095,23 @@ body {
.subtitle-container {
@apply capitalize-first dark:text-gray-300 col-span-12 break-words w-full max-w-[80%] sm:max-w-[800px] leading-normal text-[1.1rem] mb-0 lowercase;
@apply capitalize-first break-word dark:text-gray-300 col-span-12 break-words w-full max-w-[80%] sm:max-w-[800px] leading-normal text-[1.1rem] mb-0 lowercase;
}
.subtitle-card {
@apply capitalize-first dark:text-gray-300 col-span-12 break-words w-full max-w-[80%] sm:max-w-[700px] leading-normal text-base mb-0 lowercase;
@apply capitalize-first break-word dark:text-gray-300 col-span-12 break-words w-full max-w-[80%] sm:max-w-[700px] leading-normal text-base mb-0 lowercase;
}
.subtitle-content {
@apply capitalize-first dark:text-gray-300 col-span-12 break-words w-full max-w-[80%] sm:max-w-[700px] leading-normal text-[0.975rem] mb-0 lowercase;
@apply capitalize-first break-word dark:text-gray-300 col-span-12 break-words w-full max-w-[80%] sm:max-w-[700px] leading-normal text-[0.975rem] mb-0 lowercase;
}
.subtitle-min {
@apply capitalize-first dark:text-gray-300 col-span-12 break-words w-full max-w-[80%] sm:max-w-[700px] leading-normal text-[0.9rem] mb-0 lowercase;
@apply capitalize-first break-word dark:text-gray-300 col-span-12 break-words w-full max-w-[80%] sm:max-w-[700px] leading-normal text-[0.9rem] mb-0 lowercase;
}
.subtitle-stat {
@apply capitalize-first dark:text-gray-300 col-span-12 break-words w-full max-w-[80%] sm:max-w-[600px] font-bold leading-normal text-sm mb-0 lowercase;
@apply capitalize-first break-word dark:text-gray-300 col-span-12 break-words w-full max-w-[80%] sm:max-w-[600px] font-bold leading-normal text-sm mb-0 lowercase;
}
/* STAT COMPONENT */

File diff suppressed because one or more lines are too long

View file

@ -2,7 +2,7 @@
// Containers
import Grid from "@components/Widget/Grid.vue";
import GridLayout from "@components/Widget/GridLayout.vue";
import PluginBox from "@components/Widget/PluginBox.vue";
import ListDetails from "@components/List/Details.vue";
import Title from "@components/Widget/Title.vue";
/**
@ -22,12 +22,9 @@ import Title from "@components/Widget/Title.vue";
},
},
{
type: "PluginBox",
type: "ListDetails",
data: {
name: "Plugin name",
flexClass : "justify-center",
columns: { pc: 4, tablet: 6, mobile: 12 },
containerClass: "mb-4",
text: "Plugin name",
popovers: [
{
text: "This is a popover text",
@ -72,7 +69,10 @@ const props = defineProps({
<!-- widget element -->
<template v-for="(widget, index) in container.widgets" :key="index">
<Title v-if="widget.type === 'Title'" v-bind="widget.data" />
<PluginBox v-if="widget.type === 'PluginBox'" v-bind="widget.data" />
<ListDetails
v-if="widget.type === 'ListDetails'"
v-bind="widget.data"
/>
</template>
</Grid>
</GridLayout>

View file

@ -42,6 +42,14 @@ const comboboxTemplate = {
label: "dashboard_templates",
columns: { pc: 3, tablet: 12, mobile: 12 },
containerClass: "setting",
popovers: [
{
text: "inp_templates_desc",
iconName: "info",
iconColor: "info",
svgSize: "sm",
},
],
};
const comboboxModes = {

View file

@ -40,7 +40,7 @@ onMounted(() => {
data-svg="crown"
role="img"
:aria-labelledby="icon.id"
:class="[props.iconClass, props.iconColor]"
:class="[props.iconClass, props.iconColor, 'scale-95']"
viewBox="0 0 48 46"
fill="none"
xmlns="http://www.w3.org/2000/svg"

View file

@ -28,15 +28,12 @@ const props = defineProps({
data-svg="redirect"
role="img"
aria-hidden="true"
:class="[props.iconClass, props.iconColor]"
:class="[props.iconClass, props.iconColor, 'scale-90']"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
viewBox="0 0 448 512"
>
<path
fill-rule="evenodd"
d="M15.75 2.25H21a.75.75 0 0 1 .75.75v5.25a.75.75 0 0 1-1.5 0V4.81L8.03 17.03a.75.75 0 0 1-1.06-1.06L19.19 3.75h-3.44a.75.75 0 0 1 0-1.5Zm-10.5 4.5a1.5 1.5 0 0 0-1.5 1.5v10.5a1.5 1.5 0 0 0 1.5 1.5h10.5a1.5 1.5 0 0 0 1.5-1.5V10.5a.75.75 0 0 1 1.5 0v8.25a3 3 0 0 1-3 3H5.25a3 3 0 0 1-3-3V8.25a3 3 0 0 1 3-3h8.25a.75.75 0 0 1 0 1.5H5.25Z"
clip-rule="evenodd"
/>
d="M288 32c-17.7 0-32 14.3-32 32s14.3 32 32 32h50.7L169.4 265.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L384 141.3V192c0 17.7 14.3 32 32 32s32-14.3 32-32V64c0-17.7-14.3-32-32-32H288zM80 64C35.8 64 0 99.8 0 144V400c0 44.2 35.8 80 80 80H336c44.2 0 80-35.8 80-80V320c0-17.7-14.3-32-32-32s-32 14.3-32 32v80c0 8.8-7.2 16-16 16H80c-8.8 0-16-7.2-16-16V144c0-8.8 7.2-16 16-16h80c17.7 0 32-14.3 32-32s-14.3-32-32-32H80z"
></path>
</svg>
</template>

View file

@ -1,7 +1,10 @@
<script setup>
import { defineProps, computed, reactive } from "vue";
import Flex from "@components/Widget/Flex.vue";
import PopoverGroup from "@components/Widget/PopoverGroup.vue";
import Text from "@components/Widget/Text.vue";
import Filter from "@components/Widget/Filter.vue";
import Grid from "@components/Widget/Grid.vue";
/**
@name List/Details.vue
@ -9,7 +12,7 @@ import Text from "@components/Widget/Text.vue";
@example
{
details : [{
title: "name",
text: "name",
disabled : false,
popovers: [
{
@ -24,7 +27,8 @@ import Text from "@components/Widget/Text.vue";
},
],
}]
@param {string} details - List of details item that contains a title and a list of popovers. We can also add a disabled key to disable the item.
@param {string} details - List of details item that contains a text and a list of popovers. We can also add a disabled key to disable the item.
@param {array} [filters=[]] - List of filters to apply on the list of items.
@param {columns} [columns={pc: 4, tablet: 6, mobile: 12}] - Determine the position of the items in the grid system.
*/
@ -33,6 +37,11 @@ const props = defineProps({
type: Array,
required: true,
},
filters: {
type: Array,
required: false,
default: [],
},
columns: {
type: [Object, Boolean],
required: false,
@ -43,24 +52,37 @@ const props = defineProps({
const gridClass = computed(() => {
return `col-span-${props.columns.mobile} md:col-span-${props.columns.tablet} lg:col-span-${props.columns.pc}`;
});
const data = reactive({
base: JSON.parse(JSON.stringify(props.details)),
format: JSON.parse(JSON.stringify(props.details)),
});
</script>
<template>
<ul v-if="props.details" :class="['list-details-container']">
<li
v-for="item in props.details"
:class="[
'list-details-item',
gridClass,
item.disabled ? 'disabled' : 'enabled',
]"
>
<Flex :flexClass="'justify-between items-center'">
<Text :tag="'p'" :text="props.name" />
<div>
<PopoverGroup :popovers="props.popovers" />
</div>
</Flex>
</li>
</ul>
<Grid>
<Filter
v-if="props.filters.length"
@filter="(v) => (data.format = v)"
:data="data.base"
:filters="props.filters"
/>
<ul v-if="data.format" :class="['list-details-container']">
<li
v-for="item in data.format"
:class="[
'list-details-item',
gridClass,
item.disabled ? 'disabled' : 'enabled',
]"
>
<Flex :flexClass="'justify-between items-center'">
<Text :tag="'p'" :text="item.text" />
<div>
<PopoverGroup :popovers="item.popovers" />
</div>
</Flex>
</li>
</ul>
</Grid>
</template>

View file

@ -45,16 +45,16 @@ import { useFilter } from "@utils/form.js";
},
...
]
@param {object} filters - Fields with additional data to be used as filters.
@param {object|array} data - Data object or array to filter. Emit a filter event with the filtered data.
@param {string} containerClass - Additional class for the container.
@param {array} [filters=[]] - Fields with additional data to be used as filters.
@param {object|array} [data={}] - Data object or array to filter. Emit a filter event with the filtered data.
@param {string} [containerClass=""] - Additional class for the container.
*/
const props = defineProps({
filters: {
type: Object,
type: Array,
required: false,
default: {},
default: [],
},
data: {
type: Object,

View file

@ -23,10 +23,7 @@ const props = defineProps({
</script>
<template>
<div
data-grid
:class="[props.gridClass, 'col-span-12 grid grid-cols-12 w-full relative']"
>
<div data-grid :class="[props.gridClass, 'layout-grid']">
<slot></slot>
</div>
</template>

View file

@ -66,7 +66,7 @@ const containerClass = computed(() => {
});
const gridClass = computed(() => {
return `overflow-hidden break-words grid grid-cols-12 w-full col-span-${props.columns.mobile} md:col-span-${props.columns.tablet} lg:col-span-${props.columns.pc}`;
return ` col-span-${props.columns.mobile} md:col-span-${props.columns.tablet} lg:col-span-${props.columns.pc}`;
});
const gridLayoutEl = ref();
@ -88,7 +88,12 @@ onMounted(() => {
ref="gridLayoutEl"
:is="props.link ? 'a' : 'div'"
data-grid-layout
:class="[containerClass, gridClass, props.gridLayoutClass, 'p-4']"
:class="[
containerClass,
gridClass,
props.gridLayoutClass,
'layout-grid-layout',
]"
>
<slot></slot>
</component>

View file

@ -67,7 +67,7 @@ import Filter from "@components/Widget/Filter.vue";
@param {array} header - Determine the header of the table.
@param {array} positions - Determine the position of each item in the table in a list of number based on 12 columns grid.
@param {array} items - items to render in the table. This need to be an array (row) of array (cols) with a cell being a regular widget.
@param {array} filters - Determine the filters of the table.
@param {array} [filters=[]] - Determine the filters of the table.
@param {string} [minWidth="base"] - Determine the minimum size of the table. Can be "base", "sm", "md", "lg", "xl".
@param {string} [containerClass=""] - Container additional class.
@param {string} [containerWrapClass=""] - Container wrap additional class.
@ -80,9 +80,9 @@ const props = defineProps({
required: true,
},
filters: {
type: Object,
type: Array,
required: false,
default: {},
default: [],
},
minWidth: {
type: String,

View file

@ -108,8 +108,8 @@
"inp_select_dropdown_desc": "Radio group (dropdown) to change value.",
"inp_input_password_desc": "Toggle hide/show password.",
"inp_combobox_placeholder": "Search",
"inp_search_settings": "Search settings",
"inp_keyword": "keyword",
"inp_search_settings": "Search settings",
"inp_search_settings_desc": "Search within description, setting name or setting id (SETTING_ID)",
"inp_combobox_advanced_desc": "Switch between available plugins.",
"inp_select_plugin_type": "plugin type",
@ -200,5 +200,13 @@
"jobs_table_reload" : "Reload",
"jobs_table_success" : "Success",
"jobs_table_last_run_date" : "Last run date",
"jobs_table_cache_downloadable" : "Cache (downloadable)"
"jobs_table_cache_downloadable" : "Cache (downloadable)",
"plugins_pro_plugin": "Pro plugin",
"plugins_core_plugin": "Core plugin",
"plugins_external_plugin": "External plugin",
"plugins_redirect_page": "Redirect to plugin page",
"plugins_search": "Search plugin",
"plugins_search_desc": "Search the plugin by his name",
"plugins_type": "Plugin type",
"plugins_type_desc": "Only show plugins of the chosen type"
}

View file

@ -1,8 +1,7 @@
<script setup>
import { reactive, onBeforeMount, onMounted } from "vue";
import { reactive, onBeforeMount } from "vue";
import DashboardLayout from "@components/Dashboard/Layout.vue";
import BuilderPlugins from "@components/Builder/Plugins.vue";
import { useGlobal } from "@utils/global.js";
/**
@name Page/PLugins.vue
@ -25,10 +24,6 @@ onBeforeMount(() => {
plugins.builder = data;
});
onMounted(() => {
useGlobal();
});
const builder = [
{
type: "card",
@ -42,43 +37,102 @@ const builder = [
},
},
{
type: "PluginBox",
type: "ListDetails",
data: {
name: "Plugin name",
columns: { pc: 4, tablet: 6, mobile: 12 },
popovers: [
filters: [
{
text: "External plugin",
iconName: "external",
iconColor: "info",
filter: "details",
filterName: "keyword",
type: "keyword",
value: "",
keys: ["text"],
field: {
id: `filter-plugin-name`,
value: "",
type: "text",
name: `filter-plugin-name`,
containerClass: "setting",
label: "plugins_search",
placeholder: "inp_keyword",
isClipboard: false,
popovers: [
{
text: "plugins_search_desc",
iconName: "info",
iconColor: "info",
svgSize: "sm",
},
],
columns: { pc: 3, tablet: 4, mobile: 12 },
},
},
{
filter: "details",
filterName: "type",
type: "select",
value: "all",
keys: ["type"],
field: {
id: `filter-plugin-type`,
value: "all",
values: ["all", "pro", "core", "external"],
name: `filter-plugin-type`,
onlyDown: true,
label: "plugins_type",
containerClass: "setting",
maxBtnChars: 24,
popovers: [
{
text: "plugins_type_desc",
iconName: "info",
iconColor: "info",
svgSize: "sm",
},
],
columns: { pc: 3, tablet: 4, mobile: 12 },
},
},
],
},
},
{
type: "PluginBox",
data: {
name: "Plugin name",
columns: { pc: 4, tablet: 6, mobile: 12 },
popovers: [
details: [
{
text: "Pro plugin",
iconName: "crown",
iconColor: "amber",
text: "Pro",
type: "pro",
disabled: true,
popovers: [
{
text: "plugins_pro_plugin",
iconName: "crown",
iconColor: "amber",
},
],
},
],
},
},
{
type: "PluginBox",
data: {
name: "Plugin name",
columns: { pc: 4, tablet: 6, mobile: 12 },
popovers: [
{
text: "Core plugin",
iconName: "core",
iconColor: "red",
text: "Core",
type: "core",
popovers: [
{
text: "plugins_core_plugin",
iconName: "core",
iconColor: "red",
},
],
},
{
text: "external",
type: "external",
popovers: [
{
text: "plugins_redirect_page",
iconName: "redirect",
iconColor: "info",
},
{
text: "plugins_external_plugin",
iconName: "external",
iconColor: "blue",
},
],
},
],
},

View file

@ -236,6 +236,8 @@ export default {
"sm:p-2",
"md:p-2",
"lg:p-2",
"scale-90",
"scale-95",
],
important: true,
darkMode: "class",