Merge branch 'prod'

pull/6319/head
Cagatay Civici 2024-08-30 00:44:13 +03:00
commit dcd66c1515
6 changed files with 513 additions and 180 deletions

View File

@ -7,9 +7,9 @@
<template #list="slotProps"> <template #list="slotProps">
<div class="flex flex-col"> <div class="flex flex-col">
<div v-for="(item, index) in slotProps.items" :key="index"> <div v-for="(item, index) in slotProps.items" :key="index">
<div class="flex flex-col sm:flex-row sm:items-center p-6 gap-4" :class="{ 'border-t border-surface-200 dark:border-surface-700': index !== 0 }"> <div class="flex flex-col sm:flex-row sm:items-center gap-4">
<div class="md:w-40 relative"> <div class="md:w-40 relative">
<img class="block xl:block mx-auto rounded w-full" :src="`https://primefaces.org/cdn/primevue/images/product/${item.image}`" :alt="item.name" /> <img class="rounded w-36" :src="`https://primefaces.org/cdn/primevue/images/product/${item.image}`" :alt="item.name" />
<div class="dark:bg-surface-900 absolute rounded-border" style="left: 4px; top: 4px"> <div class="dark:bg-surface-900 absolute rounded-border" style="left: 4px; top: 4px">
<Tag :value="item.inventoryStatus" :severity="getSeverity(item)"></Tag> <Tag :value="item.inventoryStatus" :severity="getSeverity(item)"></Tag>
</div> </div>

View File

@ -1,9 +1,10 @@
<template> <template>
<DocSectionText v-bind="$attrs"> <DocSectionText v-bind="$attrs">
<p>Uploading implementation can be overridden by enabling <i>customUpload</i> property and defining a custom <i>uploader</i> handler event.</p> <p>Uploading implementation can be overridden by enabling <i>customUpload</i> property. This sample, displays the image on the client side with a grayscale filter.</p>
</DocSectionText> </DocSectionText>
<div class="card flex justify-center"> <div class="card flex flex-col items-center gap-6">
<FileUpload mode="basic" name="demo[]" url="/api/upload" accept="image/*" customUpload @uploader="customBase64Uploader" /> <FileUpload mode="basic" @select="onFileSelect" customUpload auto severity="secondary" class="p-button-outlined" />
<img v-if="src" :src="src" alt="Image" class="shadow-md rounded-xl w-full sm:w-64" style="filter: grayscale(100%)" />
</div> </div>
<DocSectionCode :code="code" /> <DocSectionCode :code="code" />
</template> </template>
@ -12,30 +13,37 @@
export default { export default {
data() { data() {
return { return {
src: null,
code: { code: {
basic: ` basic: `
<FileUpload mode="basic" name="demo[]" url="/api/upload" accept="image/*" customUpload @uploader="customBase64Uploader" /> <FileUpload mode="basic" @select="onFileSelect" customUpload auto severity="secondary" class="p-button-outlined" />
<img v-if="src" :src="src" alt="Image" class="shadow-md rounded-xl w-full sm:w-64" style="filter: grayscale(100%)" />
`, `,
options: ` options: `
<template> <template>
<div class="card flex justify-center"> <div class="card flex flex-col items-center gap-6">
<FileUpload mode="basic" name="demo[]" url="/api/upload" accept="image/*" customUpload @uploader="customBase64Uploader" /> <FileUpload mode="basic" @select="onFileSelect" customUpload auto severity="secondary" class="p-button-outlined" />
<img v-if="src" :src="src" alt="Image" class="shadow-md rounded-xl w-full sm:w-64" style="filter: grayscale(100%)" />
</div> </div>
</template> </template>
<script> <script>
export default { export default {
data() {
return {
src: null
}
},
methods: { methods: {
async customBase64Uploader(event) { onFileSelect(event) {
const file = event.files[0]; const file = event.files[0];
const reader = new FileReader(); const reader = new FileReader();
let blob = await fetch(file.objectURL).then((r) => r.blob()); //blob:url
reader.readAsDataURL(blob); reader.onload = async (e) => {
this.src = e.target.result;
reader.onloadend = function () {
const base64data = reader.result;
}; };
reader.readAsDataURL(file);
} }
} }
}; };
@ -43,39 +51,42 @@ export default {
`, `,
composition: ` composition: `
<template> <template>
<div class="card flex justify-center"> <div class="card flex flex-col items-center gap-6">
<FileUpload mode="basic" name="demo[]" url="/api/upload" accept="image/*" customUpload @uploader="customBase64Uploader" /> <FileUpload mode="basic" @select="onFileSelect" customUpload auto severity="secondary" class="p-button-outlined" />
<img v-if="src" :src="src" alt="Image" class="shadow-md rounded-xl w-full sm:w-64" style="filter: grayscale(100%)" />
</div> </div>
</template> </template>
<script setup> <script setup>
const customBase64Uploader = async (event) => { import { ref } from "vue";
const src = ref(null);
function onFileSelect(event) {
const file = event.files[0]; const file = event.files[0];
const reader = new FileReader(); const reader = new FileReader();
let blob = await fetch(file.objectURL).then((r) => r.blob()); //blob:url
reader.readAsDataURL(blob); reader.onload = async (e) => {
src.value = e.target.result;
reader.onloadend = function () {
const base64data = reader.result;
}; };
};
reader.readAsDataURL(file);
}
<\/script> <\/script>
` `
} }
}; };
}, },
methods: { methods: {
async customBase64Uploader(event) { onFileSelect(event) {
const file = event.files[0]; const file = event.files[0];
const reader = new FileReader(); const reader = new FileReader();
let blob = await fetch(file.objectURL).then((r) => r.blob()); //blob:url
reader.readAsDataURL(blob); reader.onload = async (e) => {
this.src = e.target.result;
reader.onloadend = function () {
const base64data = reader.result;
}; };
reader.readAsDataURL(file);
} }
} }
}; };

View File

@ -1,34 +1,55 @@
<template> <template>
<DocSectionText v-bind="$attrs"> <DocSectionText v-bind="$attrs">
<p>An example that displays a DataTable inside a popup to select an item.</p> <p>Place the Popover outside of the data iteration components to avoid rendering it multiple times.</p>
</DocSectionText> </DocSectionText>
<div class="card flex flex-col items-center gap-4"> <div class="card">
<Button type="button" icon="pi pi-search" :label="selectedProduct ? selectedProduct.name : 'Select a Product'" @click="toggle" aria-haspopup="true" aria-controls="overlay_panel" /> <DataTable :value="products" :rows="5" paginator tableStyle="min-width: 50rem">
<Column field="id" header="Id" class="w-1/6"></Column>
<Column field="code" header="Code" class="w-1/6"></Column>
<Column field="name" header="Name" class="w-1/6" bodyClass="whitespace-nowrap"></Column>
<Column field="price" header="Price" sortable class="w-1/6">
<template #body="slotProps"> $ {{ slotProps.data.price }} </template>
</Column>
<Column header="Image" class="w-1/6">
<template #body="slotProps">
<img :src="`https://primefaces.org/cdn/primevue/images/product/${slotProps.data.image}`" :alt="slotProps.data.image" class="w-16 shadow-sm" />
</template>
</Column>
<Column header="Details" class="w-1/6">
<template #body="slotProps">
<Button type="button" @click="displayProduct($event, slotProps.data)" icon="pi pi-search" severity="secondary" rounded></Button>
</template>
</Column>
</DataTable>
<div v-if="selectedProduct" class="p-8 bg-surface-0 dark:bg-surface-900 rounded border border-surface-200 dark:border-surface-700"> <Popover ref="op">
<div class="relative"> <div v-if="selectedProduct" class="rounded flex flex-col">
<img :src="`https://primefaces.org/cdn/primevue/images/product/${selectedProduct.image}`" :alt="selectedProduct.name" class="w-full sm:w-80" /> <div class="flex justify-center rounded">
<div class="relative mx-auto">
<img class="rounded w-44 sm:w-64" :src="`https://primefaces.org/cdn/primevue/images/product/${selectedProduct.image}`" :alt="selectedProduct.name" />
<Tag :value="selectedProduct.inventoryStatus" :severity="getSeverity(selectedProduct)" class="absolute dark:!bg-surface-900" style="left: 4px; top: 4px"></Tag>
</div>
</div>
<div class="pt-4">
<div class="flex flex-row justify-between items-start gap-2 mb-4">
<div>
<span class="font-medium text-surface-500 dark:text-surface-400 text-sm">{{ selectedProduct.category }}</span>
<div class="text-lg font-medium mt-1">{{ selectedProduct.name }}</div>
</div>
<div class="bg-surface-100 p-1" style="border-radius: 30px">
<div class="bg-surface-0 flex items-center gap-2 justify-center py-1 px-2" style="border-radius: 30px; box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.04), 0px 1px 2px 0px rgba(0, 0, 0, 0.06)">
<span class="text-surface-900 font-medium text-sm">{{ selectedProduct.rating }}</span>
<i class="pi pi-star-fill text-yellow-500"></i>
</div>
</div>
</div>
<div class="flex gap-2">
<Button icon="pi pi-shopping-cart" :label="`Buy Now | \$${selectedProduct.price}`" :disabled="selectedProduct.inventoryStatus === 'OUTOFSTOCK'" class="flex-auto whitespace-nowrap" @click="hidePopover"></Button>
<Button icon="pi pi-heart" outlined @click="hidePopover"></Button>
</div>
</div>
</div> </div>
<div class="flex items-center justify-between mt-4 mb-2">
<span class="font-semibold text-xl">{{ selectedProduct.name }}</span>
<span class="text-xl ml-4">{{ '$' + selectedProduct.price }}</span>
</div>
<span class="text-surface-500 dark:text-surface-400">{{ selectedProduct.category }}</span>
</div>
<Popover ref="op" appendTo="body">
<DataTable v-model:selection="selectedProduct" :value="products" selectionMode="single" :paginator="true" :rows="5" @row-select="onProductSelect">
<Column field="name" header="Name" sortable style="min-width: 12rem"></Column>
<Column header="Image">
<template #body="slotProps">
<img :src="`https://primefaces.org/cdn/primevue/images/product/${slotProps.data.image}`" :alt="slotProps.data.image" class="w-16 shadow-sm" />
</template>
</Column>
<Column field="price" header="Price" sortable style="min-width: 8rem">
<template #body="slotProps"> $ {{ slotProps.data.price }} </template>
</Column>
</DataTable>
</Popover> </Popover>
</div> </div>
<DocSectionCode :code="code" :service="['ProductService']" /> <DocSectionCode :code="code" :service="['ProductService']" />
@ -43,67 +64,103 @@ export default {
selectedProduct: null, selectedProduct: null,
code: { code: {
basic: ` basic: `
<Toast /> <DataTable :value="products" :rows="5" paginator tableStyle="min-width: 50rem">
<Button type="button" icon="pi pi-search" :label="selectedProduct ? selectedProduct.name : 'Select a Product'" @click="toggle" aria-haspopup="true" aria-controls="overlay_panel" /> <Column field="id" header="Id" class="w-1/6"></Column>
<Column field="code" header="Code" class="w-1/6"></Column>
<Column field="name" header="Name" class="w-1/6" bodyClass="whitespace-nowrap"></Column>
<Column field="price" header="Price" sortable class="w-1/6">
<template #body="slotProps"> $ {{ slotProps.data.price }} </template>
</Column>
<Column header="Image" class="w-1/6">
<template #body="slotProps">
<img :src="\`https://primefaces.org/cdn/primevue/images/product/\${slotProps.data.image}\`" :alt="slotProps.data.image" class="w-16 shadow-sm" />
</template>
</Column>
<Column header="Details" class="w-1/6">
<template #body="slotProps">
<Button type="button" @click="displayProduct($event, slotProps.data)" icon="pi pi-search" severity="secondary" rounded></Button>
</template>
</Column>
</DataTable>
<div v-if="selectedProduct" class="p-8 bg-surface-0 dark:bg-surface-900 rounded border border-surface-200 dark:border-surface-700"> <Popover ref="op">
<div class="relative"> <div v-if="selectedProduct" class="rounded flex flex-col">
<img :src="\`/images/product/\${selectedProduct.image}\`" :alt="selectedProduct.name" class="w-16 shadow-sm" class="w-full sm:w-80" /> <div class="flex justify-center rounded">
<div class="relative mx-auto">
<img class="rounded w-44 sm:w-64" :src="\`https://primefaces.org/cdn/primevue/images/product/\${selectedProduct.image}\`" :alt="selectedProduct.name" />
<Tag :value="selectedProduct.inventoryStatus" :severity="getSeverity(selectedProduct)" class="absolute dark:!bg-surface-900" style="left: 4px; top: 4px"></Tag>
</div>
</div>
<div class="pt-4">
<div class="flex flex-row justify-between items-start gap-2 mb-4">
<div>
<span class="font-medium text-surface-500 dark:text-surface-400 text-sm">{{ selectedProduct.category }}</span>
<div class="text-lg font-medium mt-1">{{ selectedProduct.name }}</div>
</div>
<div class="bg-surface-100 p-1" style="border-radius: 30px">
<div class="bg-surface-0 flex items-center gap-2 justify-center py-1 px-2" style="border-radius: 30px; box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.04), 0px 1px 2px 0px rgba(0, 0, 0, 0.06)">
<span class="text-surface-900 font-medium text-sm">{{ selectedProduct.rating }}</span>
<i class="pi pi-star-fill text-yellow-500"></i>
</div>
</div>
</div>
<div class="flex gap-2">
<Button icon="pi pi-shopping-cart" :label="\`Buy Now | \\$\${selectedProduct.price}\`" :disabled="selectedProduct.inventoryStatus === 'OUTOFSTOCK'" class="flex-auto whitespace-nowrap" @click="hidePopover"></Button>
<Button icon="pi pi-heart" outlined @click="hidePopover"></Button>
</div>
</div>
</div> </div>
<div class="flex items-center justify-between mt-4 mb-2">
<span class="font-semibold text-xl">{{ selectedProduct.name }}</span>
<span class="text-xl ml-4">{{ '$' + selectedProduct.price }}</span>
</div>
<span class="text-surface-500 dark:text-surface-400">{{ selectedProduct.category }}</span>
</div>
<Popover ref="op" appendTo="body">
<DataTable v-model:selection="selectedProduct" :value="products" selectionMode="single" :paginator="true" :rows="5" @row-select="onProductSelect">
<Column field="name" header="Name" sortable style="width: 50%"></Column>
<Column header="Image" style="width: 20%">
<template #body="slotProps">
<img :src="\`/images/product/\${selectedProduct.image}\`" :alt="slotProps.data.image" class="w-16 shadow-sm" />
</template>
</Column>
<Column field="price" header="Price" sortable style="width: 30%">
<template #body="slotProps">
$ {{ slotProps.data.price }}
</template>
</Column>
</DataTable>
</Popover> </Popover>
`, `,
options: ` options: `
<template> <template>
<div class="card flex flex-col items-center gap-4"> <div class="card">
<Toast /> <DataTable :value="products" :rows="5" paginator tableStyle="min-width: 50rem">
<Button type="button" icon="pi pi-search" :label="selectedProduct ? selectedProduct.name : 'Select a Product'" @click="toggle" aria-haspopup="true" aria-controls="overlay_panel" /> <Column field="id" header="Id" class="w-1/6"></Column>
<Column field="code" header="Code" class="w-1/6"></Column>
<Column field="name" header="Name" class="w-1/6" bodyClass="whitespace-nowrap"></Column>
<Column field="price" header="Price" sortable class="w-1/6">
<template #body="slotProps"> $ {{ slotProps.data.price }} </template>
</Column>
<Column header="Image" class="w-1/6">
<template #body="slotProps">
<img :src="\`https://primefaces.org/cdn/primevue/images/product/\${slotProps.data.image}\`" :alt="slotProps.data.image" class="w-16 shadow-sm" />
</template>
</Column>
<Column header="Details" class="w-1/6">
<template #body="slotProps">
<Button type="button" @click="displayProduct($event, slotProps.data)" icon="pi pi-search" severity="secondary" rounded></Button>
</template>
</Column>
</DataTable>
<div v-if="selectedProduct" class="p-8 bg-surface-0 dark:bg-surface-900 rounded border border-surface-200 dark:border-surface-700"> <Popover ref="op">
<div class="relative"> <div v-if="selectedProduct" class="rounded flex flex-col">
<img :src="\`https://primefaces.org/cdn/primevue/images/product/\${selectedProduct.image}\`" :alt="selectedProduct.name" class="w-full sm:w-80" /> <div class="flex justify-center rounded">
<div class="relative mx-auto">
<img class="rounded w-44 sm:w-64" :src="\`https://primefaces.org/cdn/primevue/images/product/\${selectedProduct.image}\`" :alt="selectedProduct.name" />
<Tag :value="selectedProduct.inventoryStatus" :severity="getSeverity(selectedProduct)" class="absolute dark:!bg-surface-900" style="left: 4px; top: 4px"></Tag>
</div>
</div>
<div class="pt-4">
<div class="flex flex-row justify-between items-start gap-2 mb-4">
<div>
<span class="font-medium text-surface-500 dark:text-surface-400 text-sm">{{ selectedProduct.category }}</span>
<div class="text-lg font-medium mt-1">{{ selectedProduct.name }}</div>
</div>
<div class="bg-surface-100 p-1" style="border-radius: 30px">
<div class="bg-surface-0 flex items-center gap-2 justify-center py-1 px-2" style="border-radius: 30px; box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.04), 0px 1px 2px 0px rgba(0, 0, 0, 0.06)">
<span class="text-surface-900 font-medium text-sm">{{ selectedProduct.rating }}</span>
<i class="pi pi-star-fill text-yellow-500"></i>
</div>
</div>
</div>
<div class="flex gap-2">
<Button icon="pi pi-shopping-cart" :label="\`Buy Now | \\$\${selectedProduct.price}\`" :disabled="selectedProduct.inventoryStatus === 'OUTOFSTOCK'" class="flex-auto whitespace-nowrap" @click="hidePopover"></Button>
<Button icon="pi pi-heart" outlined @click="hidePopover"></Button>
</div>
</div>
</div> </div>
<div class="flex items-center justify-between mt-4 mb-2">
<span class="font-semibold text-xl">{{ selectedProduct.name }}</span>
<span class="text-xl ml-4">{{ '$' + selectedProduct.price }}</span>
</div>
<span class="text-surface-500 dark:text-surface-400">{{ selectedProduct.category }}</span>
</div>
<Popover ref="op" appendTo="body">
<DataTable v-model:selection="selectedProduct" :value="products" selectionMode="single" :paginator="true" :rows="5" @row-select="onProductSelect">
<Column field="name" header="Name" sortable style="min-width: 12rem"></Column>
<Column header="Image">
<template #body="slotProps">
<img :src="\`https://primefaces.org/cdn/primevue/images/product/\${selectedProduct.image}\`" :alt="slotProps.data.image" class="w-16 shadow-sm" />
</template>
</Column>
<Column field="price" header="Price" sortable style="min-width: 8rem">
<template #body="slotProps">
$ {{ slotProps.data.price }}
</template>
</Column>
</DataTable>
</Popover> </Popover>
</div> </div>
</template> </template>
@ -119,17 +176,40 @@ export default {
}; };
}, },
mounted() { mounted() {
ProductService.getProductsSmall() ProductService.getProductsSmall().then((data) => (this.products = data));
.then((data) => (this.products = data))
.then(() => (this.selectedProduct = this.products[0]));
}, },
methods: { methods: {
toggle(event) { displayProduct(event, product) {
this.$refs.op.toggle(event); if (this.selectedProduct?.id === product.id) {
this.$refs.op.hide();
this.selectedProduct = null;
} else {
this.selectedProduct = product;
this.$refs.op.show(event);
// will not be needed with v4.0.6 as Popover will auto align
if (this.$refs.op.container) {
this.$refs.op.alignOverlay();
}
}
}, },
onProductSelect(event) { hidePopover() {
this.$refs.op.hide(); this.$refs.op.hide();
this.$toast.add({ severity: 'info', summary: 'Product Selected', detail: event.data.name, life: 3000 }); },
getSeverity(product) {
switch (product.inventoryStatus) {
case 'INSTOCK':
return 'success';
case 'LOWSTOCK':
return 'warn';
case 'OUTOFSTOCK':
return 'danger';
default:
return null;
}
} }
} }
}; };
@ -137,35 +217,53 @@ export default {
`, `,
composition: ` composition: `
<template> <template>
<div class="card flex flex-col items-center gap-4"> <div class="card">
<Toast /> <DataTable :value="products" :rows="5" paginator tableStyle="min-width: 50rem">
<Button type="button" icon="pi pi-search" :label="selectedProduct ? selectedProduct.name : 'Select a Product'" @click="toggle" aria-haspopup="true" aria-controls="overlay_panel" /> <Column field="id" header="Id" class="w-1/6"></Column>
<Column field="code" header="Code" class="w-1/6"></Column>
<Column field="name" header="Name" class="w-1/6" bodyClass="whitespace-nowrap"></Column>
<Column field="price" header="Price" sortable class="w-1/6">
<template #body="slotProps"> $ {{ slotProps.data.price }} </template>
</Column>
<Column header="Image" class="w-1/6">
<template #body="slotProps">
<img :src="\`https://primefaces.org/cdn/primevue/images/product/\${slotProps.data.image}\`" :alt="slotProps.data.image" class="w-16 shadow-sm" />
</template>
</Column>
<Column header="Details" class="w-1/6">
<template #body="slotProps">
<Button type="button" @click="displayProduct($event, slotProps.data)" icon="pi pi-search" severity="secondary" rounded></Button>
</template>
</Column>
</DataTable>
<div v-if="selectedProduct" class="p-8 bg-surface-0 dark:bg-surface-900 rounded border border-surface-200 dark:border-surface-700"> <Popover ref="op">
<div class="relative"> <div v-if="selectedProduct" class="rounded flex flex-col">
<img :src="\`https://primefaces.org/cdn/primevue/images/product/\${selectedProduct.image}\`" :alt="selectedProduct.name" class="w-full sm:w-80" /> <div class="flex justify-center rounded">
<div class="relative mx-auto">
<img class="rounded w-44 sm:w-64" :src="\`https://primefaces.org/cdn/primevue/images/product/\${selectedProduct.image}\`" :alt="selectedProduct.name" />
<Tag :value="selectedProduct.inventoryStatus" :severity="getSeverity(selectedProduct)" class="absolute dark:!bg-surface-900" style="left: 4px; top: 4px"></Tag>
</div>
</div>
<div class="pt-4">
<div class="flex flex-row justify-between items-start gap-2 mb-4">
<div>
<span class="font-medium text-surface-500 dark:text-surface-400 text-sm">{{ selectedProduct.category }}</span>
<div class="text-lg font-medium mt-1">{{ selectedProduct.name }}</div>
</div>
<div class="bg-surface-100 p-1" style="border-radius: 30px">
<div class="bg-surface-0 flex items-center gap-2 justify-center py-1 px-2" style="border-radius: 30px; box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.04), 0px 1px 2px 0px rgba(0, 0, 0, 0.06)">
<span class="text-surface-900 font-medium text-sm">{{ selectedProduct.rating }}</span>
<i class="pi pi-star-fill text-yellow-500"></i>
</div>
</div>
</div>
<div class="flex gap-2">
<Button icon="pi pi-shopping-cart" :label="\`Buy Now | \\$\${selectedProduct.price}\`" :disabled="selectedProduct.inventoryStatus === 'OUTOFSTOCK'" class="flex-auto whitespace-nowrap" @click="hidePopover"></Button>
<Button icon="pi pi-heart" outlined @click="hidePopover"></Button>
</div>
</div>
</div> </div>
<div class="flex items-center justify-between mt-4 mb-2">
<span class="font-semibold text-xl">{{ selectedProduct.name }}</span>
<span class="text-xl ml-4">{{ '$' + selectedProduct.price }}</span>
</div>
<span class="text-surface-500 dark:text-surface-400">{{ selectedProduct.category }}</span>
</div>
<Popover ref="op" appendTo="body">
<DataTable v-model:selection="selectedProduct" :value="products" selectionMode="single" :paginator="true" :rows="5" @row-select="onProductSelect">
<Column field="name" header="Name" sortable style="min-width: 12rem"></Column>
<Column header="Image">
<template #body="slotProps">
<img :src="\`https://primefaces.org/cdn/primevue/images/product/\${selectedProduct.image}\`" :alt="slotProps.data.image" class="w-16 shadow-sm" />
</template>
</Column>
<Column field="price" header="Price" sortable style="min-width: 8rem">
<template #body="slotProps">
$ {{ slotProps.data.price }}
</template>
</Column>
</DataTable>
</Popover> </Popover>
</div> </div>
</template> </template>
@ -176,23 +274,46 @@ import { useToast } from "primevue/usetoast";
import { ProductService } from '@/service/ProductService'; import { ProductService } from '@/service/ProductService';
onMounted(() => { onMounted(() => {
ProductService.getProductsSmall() ProductService.getProductsSmall().then((data) => (products.value = data));
.then((data) => (products.value = data))
.then(() => (selectedProduct.value = products.value[0]));
}); });
const toast = useToast();
const op = ref(); const op = ref();
const products = ref(); const products = ref();
const selectedProduct = ref(); const selectedProduct = ref();
const toggle = (event) => { const displayProduct = (event, product) => {
op.value.toggle(event); if (selectedProduct.value?.id === product.id) {
}; op.value.hide();
selectedProduct.value = null;
} else {
selectedProduct.value = product;
op.value.show(event);
const onProductSelect = (event) => { // will not be needed with v4.0.6 as Popover will auto align
if (op.value.container) {
op.value.alignOverlay();
}
}
}
const hidePopover = () => {
op.value.hide(); op.value.hide();
toast.add({ severity: 'info', summary: 'Product Selected', detail: event.data.name, life: 3000 }); }
const getSeverity = (product) => {
switch (product.inventoryStatus) {
case 'INSTOCK':
return 'success';
case 'LOWSTOCK':
return 'warn';
case 'OUTOFSTOCK':
return 'danger';
default:
return null;
}
} }
<\/script> <\/script>
`, `,
@ -216,17 +337,39 @@ const onProductSelect = (event) => {
}; };
}, },
mounted() { mounted() {
ProductService.getProductsSmall() ProductService.getProductsSmall().then((data) => (this.products = data));
.then((data) => (this.products = data))
.then(() => (this.selectedProduct = this.products[0]));
}, },
methods: { methods: {
toggle(event) { displayProduct(event, product) {
this.$refs.op.toggle(event); if (this.selectedProduct?.id === product.id) {
this.$refs.op.hide();
this.selectedProduct = null;
} else {
this.selectedProduct = product;
this.$refs.op.show(event);
if (this.$refs.op.container) {
this.$refs.op.alignOverlay();
}
}
}, },
onProductSelect(event) { hidePopover() {
this.$refs.op.hide(); this.$refs.op.hide();
this.$toast.add({ severity: 'info', summary: 'Product Selected', detail: event.data.name, life: 3000 }); },
getSeverity(product) {
switch (product.inventoryStatus) {
case 'INSTOCK':
return 'success';
case 'LOWSTOCK':
return 'warn';
case 'OUTOFSTOCK':
return 'danger';
default:
return null;
}
} }
} }
}; };

View File

@ -0,0 +1,165 @@
<template>
<DocSectionText v-bind="$attrs">
<p>In this sample, data is retrieved from the content inside the popover.</p>
</DocSectionText>
<div class="card flex justify-center">
<Button type="button" :label="selectedMember ? selectedMember.name : 'Select Member'" @click="toggle" class="min-w-48" />
<Popover ref="op">
<div class="flex flex-col gap-4">
<div>
<span class="font-medium block mb-2">Team Members</span>
<ul class="list-none p-0 m-0 flex flex-col">
<li v-for="member in members" :key="member.name" class="flex items-center gap-2 px-2 py-3 hover:bg-emphasis cursor-pointer rounded-border" @click="selectMember(member)">
<img :src="`https://primefaces.org/cdn/primevue/images/avatar/${member.image}`" style="width: 32px" />
<div>
<span class="font-medium">{{ member.name }}</span>
<div class="text-sm text-surface-500 dark:text-surface-400">{{ member.email }}</div>
</div>
</li>
</ul>
</div>
</div>
</Popover>
</div>
<DocSectionCode :code="code" />
</template>
<script>
export default {
data() {
return {
selectedMember: null,
members: [
{ name: 'Amy Elsner', image: 'amyelsner.png', email: 'amy@email.com', role: 'Owner' },
{ name: 'Bernardo Dominic', image: 'bernardodominic.png', email: 'bernardo@email.com', role: 'Editor' },
{ name: 'Ioni Bowcher', image: 'ionibowcher.png', email: 'ioni@email.com', role: 'Viewer' }
],
code: {
basic: `
<Button type="button" :label="selectedMember ? selectedMember.name : 'Select Member'" @click="toggle" class="min-w-48" />
<Popover ref="op">
<div class="flex flex-col gap-4">
<div>
<span class="font-medium block mb-2">Team Members</span>
<ul class="list-none p-0 m-0 flex flex-col">
<li v-for="member in members" :key="member.name" class="flex items-center gap-2 px-2 py-3 hover:bg-emphasis cursor-pointer rounded-border" @click="selectMember(member)">
<img :src="\`https://primefaces.org/cdn/primevue/images/avatar/\${member.image}\`" style="width: 32px" />
<div>
<span class="font-medium">{{ member.name }}</span>
<div class="text-sm text-surface-500 dark:text-surface-400">{{ member.email }}</div>
</div>
</li>
</ul>
</div>
</div>
</Popover>
`,
options: `
<template>
<div class="card flex justify-center">
<Button type="button" :label="selectedMember ? selectedMember.name : 'Select Member'" @click="toggle" class="min-w-48" />
<Popover ref="op">
<div class="flex flex-col gap-4">
<div>
<span class="font-medium block mb-2">Team Members</span>
<ul class="list-none p-0 m-0 flex flex-col">
<li v-for="member in members" :key="member.name" class="flex items-center gap-2 px-2 py-3 hover:bg-emphasis cursor-pointer rounded-border" @click="selectMember(member)">
<img :src="\`https://primefaces.org/cdn/primevue/images/avatar/\${member.image}\`" style="width: 32px" />
<div>
<span class="font-medium">{{ member.name }}</span>
<div class="text-sm text-surface-500 dark:text-surface-400">{{ member.email }}</div>
</div>
</li>
</ul>
</div>
</div>
</Popover>
</div>
</template>
<script>
export default {
data() {
return {
selectedMember: null,
members: [
{ name: 'Amy Elsner', image: 'amyelsner.png', email: 'amy@email.com', role: 'Owner' },
{ name: 'Bernardo Dominic', image: 'bernardodominic.png', email: 'bernardo@email.com', role: 'Editor' },
{ name: 'Ioni Bowcher', image: 'ionibowcher.png', email: 'ioni@email.com', role: 'Viewer' }
]
}
},
methods: {
toggle(event) {
this.$refs.op.toggle(event);
},
selectMember(member) {
this.selectedMember = member;
this.$refs.op.hide();
}
}
};
<\/script>
`,
composition: `
<template>
<div class="card flex justify-center">
<Button type="button" :label="selectedMember ? selectedMember.name : 'Select Member'" @click="toggle" class="min-w-48" />
<Popover ref="op">
<div class="flex flex-col gap-4">
<div>
<span class="font-medium block mb-2">Team Members</span>
<ul class="list-none p-0 m-0 flex flex-col">
<li v-for="member in members" :key="member.name" class="flex items-center gap-2 px-2 py-3 hover:bg-emphasis cursor-pointer rounded-border" @click="selectMember(member)">
<img :src="\`https://primefaces.org/cdn/primevue/images/avatar/\${member.image}\`" style="width: 32px" />
<div>
<span class="font-medium">{{ member.name }}</span>
<div class="text-sm text-surface-500 dark:text-surface-400">{{ member.email }}</div>
</div>
</li>
</ul>
</div>
</div>
</Popover>
</div>
</template>
<script setup>
import { ref } from "vue";
const op = ref();
const selectedMember = ref(null);
const members = ref([
{ name: 'Amy Elsner', image: 'amyelsner.png', email: 'amy@email.com', role: 'Owner' },
{ name: 'Bernardo Dominic', image: 'bernardodominic.png', email: 'bernardo@email.com', role: 'Editor' },
{ name: 'Ioni Bowcher', image: 'ionibowcher.png', email: 'ioni@email.com', role: 'Viewer' }
]);
const toggle = (event) => {
op.value.toggle(event);
}
const selectMember = (member) => {
selectedMember.value = member;
op.value.hide();
}
<\/script>
`
}
};
},
methods: {
toggle(event) {
this.$refs.op.toggle(event);
},
selectMember(member) {
this.selectedMember = member;
this.$refs.op.hide();
}
}
};
</script>

View File

@ -16,6 +16,7 @@ import BasicDoc from '@/doc/popover/BasicDoc.vue';
import DataTableDoc from '@/doc/popover/DataTableDoc.vue'; import DataTableDoc from '@/doc/popover/DataTableDoc.vue';
import ImportDoc from '@/doc/popover/ImportDoc.vue'; import ImportDoc from '@/doc/popover/ImportDoc.vue';
import PTComponent from '@/doc/popover/pt/index.vue'; import PTComponent from '@/doc/popover/pt/index.vue';
import SelectDataDoc from '@/doc/popover/SelectDataDoc.vue';
import ThemingDoc from '@/doc/popover/theming/index.vue'; import ThemingDoc from '@/doc/popover/theming/index.vue';
export default { export default {
@ -32,6 +33,11 @@ export default {
label: 'Basic', label: 'Basic',
component: BasicDoc component: BasicDoc
}, },
{
id: 'selectdata',
label: 'Select Data',
component: SelectDataDoc
},
{ {
id: 'datatable', id: 'datatable',
label: 'DataTable', label: 'DataTable',

View File

@ -67,15 +67,24 @@
</div> </div>
<div class="flex-1 flex gap-4 flex-col"> <div class="flex-1 flex gap-4 flex-col">
<div class="p-4 bg-surface-0 dark:bg-surface-900 rounded border-blue-500 border-l-8"> <div class="p-4 bg-surface-0 dark:bg-surface-900 rounded border-blue-500 border-l-8">
<h2 class="text-lg font-bold mt-0 mb-2">New Components</h2> <h2 class="text-lg font-bold mt-0 mb-2">Form Library</h2>
<p class="mt-0 mb-4 leading-normal">Layout, Typography, Tab orientations, Navigation Drawer.</p> <p class="mt-0 mb-4 leading-normal">Built-in form library with validations.</p>
<div class="bg-surface-200 rounded">
<div class="bg-blue-500 rounded" style="width: 25%; height: 4px"></div>
</div>
</div>
</div>
<div class="flex-1 flex gap-4 flex-col">
<div class="p-4 bg-surface-0 dark:bg-surface-900 rounded border-blue-500 border-l-8">
<h2 class="text-lg font-bold mt-0 mb-2">Components</h2>
<p class="mt-0 mb-4 leading-normal">New Carousel, Tab Orientations, Updated Menu, Navigation Drawer...</p>
<div class="bg-surface-200 rounded"> <div class="bg-surface-200 rounded">
<div class="bg-blue-500 rounded" style="width: 0%; height: 4px"></div> <div class="bg-blue-500 rounded" style="width: 0%; height: 4px"></div>
</div> </div>
</div> </div>
<div class="p-4 bg-surface-0 dark:bg-surface-900 rounded border-blue-500 border-l-8"> <div class="p-4 bg-surface-0 dark:bg-surface-900 rounded border-blue-500 border-l-8">
<h2 class="text-lg font-bold mt-0 mb-2">Form Library</h2> <h2 class="text-lg font-bold mt-0 mb-2">Headless Mode</h2>
<p class="mt-0 mb-4 leading-normal">Built-in form library with validations.</p> <p class="mt-0 mb-4 leading-normal">Headless component kit with Tailwind CSS.</p>
<div class="bg-surface-200 rounded"> <div class="bg-surface-200 rounded">
<div class="bg-blue-500 rounded" style="width: 0%; height: 4px"></div> <div class="bg-blue-500 rounded" style="width: 0%; height: 4px"></div>
</div> </div>
@ -94,11 +103,9 @@
<div class="bg-blue-500 rounded" style="width: 0%; height: 4px"></div> <div class="bg-blue-500 rounded" style="width: 0%; height: 4px"></div>
</div> </div>
</div> </div>
</div>
<div class="flex-1 flex gap-4 flex-col">
<div class="p-4 bg-surface-0 dark:bg-surface-900 rounded border-blue-500 border-l-8"> <div class="p-4 bg-surface-0 dark:bg-surface-900 rounded border-blue-500 border-l-8">
<h2 class="text-lg font-bold mt-0 mb-2">Advanced Suite ... 2025</h2> <h2 class="text-lg font-bold mt-0 mb-2">PrimeVue+ Suite | 2025</h2>
<p class="mt-0 mb-4 leading-normal">Gantt Chart, Flow Chart, Sheet, Calendar, Timeline, Editor.</p> <p class="mt-0 mb-4 leading-normal">Gantt Chart, Flow Chart, Sheet, PDF Viewer, Calendar, Timeline, Editor.</p>
<div class="bg-surface-200 rounded"> <div class="bg-surface-200 rounded">
<div class="bg-blue-500 rounded" style="width: 0%; height: 4px"></div> <div class="bg-blue-500 rounded" style="width: 0%; height: 4px"></div>
</div> </div>
@ -113,7 +120,7 @@
<h2 class="text-lg font-bold mt-0 mb-2">New Figma Tokens - Phase 1</h2> <h2 class="text-lg font-bold mt-0 mb-2">New Figma Tokens - Phase 1</h2>
<p class="mt-0 mb-4 leading-normal">Update tokens to sync with the new styled mode.</p> <p class="mt-0 mb-4 leading-normal">Update tokens to sync with the new styled mode.</p>
<div class="bg-surface-200 rounded"> <div class="bg-surface-200 rounded">
<div class="bg-indigo-500 rounded" style="width: 50%; height: 4px"></div> <div class="bg-indigo-500 rounded" style="width: 100%; height: 4px"></div>
</div> </div>
</div> </div>
</div> </div>
@ -122,9 +129,11 @@
<h2 class="text-lg font-bold mt-0 mb-2">New Figma Tokens - Phase 2</h2> <h2 class="text-lg font-bold mt-0 mb-2">New Figma Tokens - Phase 2</h2>
<p class="mt-0 mb-4 leading-normal">Update tokens to sync with the new styled mode.</p> <p class="mt-0 mb-4 leading-normal">Update tokens to sync with the new styled mode.</p>
<div class="bg-surface-200 rounded"> <div class="bg-surface-200 rounded">
<div class="bg-indigo-500 rounded" style="width: 0%; height: 4px"></div> <div class="bg-indigo-500 rounded" style="width: 50%; height: 4px"></div>
</div> </div>
</div> </div>
</div>
<div class="flex-1 flex gap-4 flex-col">
<div class="p-4 bg-surface-0 dark:bg-surface-900 rounded border-indigo-500 border-l-8"> <div class="p-4 bg-surface-0 dark:bg-surface-900 rounded border-indigo-500 border-l-8">
<h2 class="text-lg font-bold mt-0 mb-2">Figma to Theme API</h2> <h2 class="text-lg font-bold mt-0 mb-2">Figma to Theme API</h2>
<p class="mt-0 mb-4 leading-normal">Build a Figma plugin to generate themes from UI Kit.</p> <p class="mt-0 mb-4 leading-normal">Build a Figma plugin to generate themes from UI Kit.</p>
@ -133,7 +142,6 @@
</div> </div>
</div> </div>
</div> </div>
<div class="flex-1 flex gap-4 flex-col"></div>
</div> </div>
<div class="flex gap-4 border-b border-surface-200 dark:border-surface-700 pb-4"> <div class="flex gap-4 border-b border-surface-200 dark:border-surface-700 pb-4">
<div class="shrink-0 p-4 bg-teal-500 text-white rounded font-bold text-lg flex items-center justify-center w-56">SHOWCASE</div> <div class="shrink-0 p-4 bg-teal-500 text-white rounded font-bold text-lg flex items-center justify-center w-56">SHOWCASE</div>
@ -144,7 +152,7 @@
<h2 class="text-lg font-bold mt-0 mb-2">Documentation</h2> <h2 class="text-lg font-bold mt-0 mb-2">Documentation</h2>
<p class="mt-0 mb-4 leading-normal">Interactive component viewer to explore tokens and pt sections.</p> <p class="mt-0 mb-4 leading-normal">Interactive component viewer to explore tokens and pt sections.</p>
<div class="bg-surface-200 rounded"> <div class="bg-surface-200 rounded">
<div class="bg-teal-500 rounded" style="width: 0%; height: 4px"></div> <div class="bg-teal-500 rounded" style="width: 25%; height: 4px"></div>
</div> </div>
</div> </div>
</div> </div>
@ -159,37 +167,37 @@
<h2 class="text-lg font-bold mt-0 mb-2">V4 Update</h2> <h2 class="text-lg font-bold mt-0 mb-2">V4 Update</h2>
<p class="mt-0 mb-4 leading-normal">Update all templates to PrimeVue v4, replace PrimeFlex demos with Tailwind.</p> <p class="mt-0 mb-4 leading-normal">Update all templates to PrimeVue v4, replace PrimeFlex demos with Tailwind.</p>
<div class="bg-surface-200 rounded"> <div class="bg-surface-200 rounded">
<div class="bg-teal-500 rounded" style="width: 0%; height: 4px"></div> <div class="bg-violet-500 rounded" style="width: 100%; height: 4px"></div>
</div> </div>
</div> </div>
</div>
<div class="flex-1 flex gap-4 flex-col">
<div class="p-4 bg-surface-0 dark:bg-surface-900 rounded border-violet-500 border-l-8"> <div class="p-4 bg-surface-0 dark:bg-surface-900 rounded border-violet-500 border-l-8">
<h2 class="text-lg font-bold mt-0 mb-2">Genesis</h2> <h2 class="text-lg font-bold mt-0 mb-2">Genesis</h2>
<p class="mt-0 mb-4 leading-normal">Brand new template application.</p> <p class="mt-0 mb-4 leading-normal">Brand new multi-purpose template.</p>
<div class="bg-surface-200 rounded"> <div class="bg-surface-200 rounded">
<div class="bg-teal-500 rounded" style="width: 0%; height: 4px"></div> <div class="bg-teal-500 rounded" style="width: 0%; height: 4px"></div>
</div> </div>
</div> </div>
</div> </div>
<div class="flex-1 flex gap-4 flex-col"></div>
</div> </div>
<div class="flex gap-4 border-b border-surface-200 dark:border-surface-700 pb-4"> <div class="flex gap-4 border-b border-surface-200 dark:border-surface-700 pb-4">
<div class="shrink-0 p-4 bg-orange-500 text-white rounded font-bold text-lg flex items-center justify-center w-56">PrimeBlocks</div> <div class="shrink-0 p-4 bg-orange-500 text-white rounded font-bold text-lg flex items-center justify-center w-56">PrimeBlocks</div>
<div class="flex-1 flex gap-4 flex-col"></div> <div class="flex-1 flex gap-4 flex-col"></div>
<div class="flex-1 flex gap-4 flex-col"></div>
<div class="flex-1 flex gap-4 flex-col"> <div class="flex-1 flex gap-4 flex-col">
<div class="p-4 bg-surface-0 dark:bg-surface-900 rounded border-orange-500 border-l-8"> <div class="p-4 bg-surface-0 dark:bg-surface-900 rounded border-orange-500 border-l-8">
<h2 class="text-lg font-bold mt-0 mb-2">Tailwind Blocks</h2> <h2 class="text-lg font-bold mt-0 mb-2">Tailwind Blocks</h2>
<p class="mt-0 mb-4 leading-normal">Migrate Blocks to Tailwind CSS.</p> <p class="mt-0 mb-4 leading-normal">Migrate Blocks to v4 and Tailwind CSS.</p>
<div class="bg-surface-200 rounded"> <div class="bg-surface-200 rounded">
<div class="bg-orange-500 rounded" style="width: 50%; height: 4px"></div> <div class="bg-orange-500 rounded" style="width: 90%; height: 4px"></div>
</div> </div>
</div> </div>
</div>
<div class="flex-1 flex gap-4 flex-col">
<div class="p-4 bg-surface-0 dark:bg-surface-900 rounded border-orange-500 border-l-8"> <div class="p-4 bg-surface-0 dark:bg-surface-900 rounded border-orange-500 border-l-8">
<h2 class="text-lg font-bold mt-0 mb-2">Online App</h2> <h2 class="text-lg font-bold mt-0 mb-2">Online App</h2>
<p class="mt-0 mb-4 leading-normal">Implement a SaaS app to access the blocks instead of an offline download.</p> <p class="mt-0 mb-4 leading-normal">Implement an app to access the blocks instead of an offline download.</p>
<div class="bg-surface-200 rounded"> <div class="bg-surface-200 rounded">
<div class="bg-orange-500 rounded" style="width: 0%; height: 4px"></div> <div class="bg-orange-500 rounded" style="width: 90%; height: 4px"></div>
</div> </div>
</div> </div>
</div> </div>
@ -199,7 +207,7 @@
<div class="shrink-0 p-4 bg-pink-500 text-white rounded font-bold text-lg flex items-center justify-center w-56">Design</div> <div class="shrink-0 p-4 bg-pink-500 text-white rounded font-bold text-lg flex items-center justify-center w-56">Design</div>
<div class="flex-1 flex gap-4 flex-col"> <div class="flex-1 flex gap-4 flex-col">
<div class="p-4 bg-surface-0 dark:bg-surface-900 rounded border-pink-500 border-l-8"> <div class="p-4 bg-surface-0 dark:bg-surface-900 rounded border-pink-500 border-l-8">
<h2 class="text-lg font-bold mt-0 mb-2">New UI Theme</h2> <h2 class="text-lg font-bold mt-0 mb-2">New Aura Theme</h2>
<p class="mt-0 mb-4 leading-normal">Brand new default theme with a modern and attractive design.</p> <p class="mt-0 mb-4 leading-normal">Brand new default theme with a modern and attractive design.</p>
<div class="bg-surface-200 rounded"> <div class="bg-surface-200 rounded">
<div class="bg-pink-500 rounded" style="width: 100%; height: 4px"></div> <div class="bg-pink-500 rounded" style="width: 100%; height: 4px"></div>
@ -207,6 +215,7 @@
</div> </div>
</div> </div>
<div class="flex-1 flex gap-4 flex-col"></div> <div class="flex-1 flex gap-4 flex-col"></div>
<div class="flex-1 flex gap-4 flex-col"></div>
<div class="flex-1 flex gap-4 flex-col"> <div class="flex-1 flex gap-4 flex-col">
<div class="flex-1 flex gap-4 flex-col"> <div class="flex-1 flex gap-4 flex-col">
<div class="p-4 bg-surface-0 dark:bg-surface-900 rounded border-pink-500 border-l-8"> <div class="p-4 bg-surface-0 dark:bg-surface-900 rounded border-pink-500 border-l-8">
@ -218,7 +227,6 @@
</div> </div>
</div> </div>
</div> </div>
<div class="flex-1 flex gap-4 flex-col"></div>
</div> </div>
</div> </div>
</div> </div>