This commit is contained in:
2025-05-24 01:49:48 +09:00
commit 62abbcf4eb
2376 changed files with 325522 additions and 0 deletions

189
src/pages/[...all].vue Normal file
View File

@@ -0,0 +1,189 @@
<script setup lang="ts">
/**
* This is a Vue Component that will be
* automatically mapped to a catch all path on vue-router (404).
*
* You will be able to access this page at http://localhost:3000/non-existing-page
*
* Read more about routing:
* @see /vite.config.ts
* @see /src/router.ts
*/
import type { VueroSSRContext } from '/@server/types'
import { setResponseHeader, setResponseStatus } from 'h3'
import { useSSRContext } from 'vue'
const { t } = useI18n()
useHead({
title: `${t('pages.not-found.page-title')} - Vuero`,
meta: [
{
name: 'robots',
content: 'noindex',
},
],
})
/**
* When the route is not found, we want to send 404 error code
* We do this by using the `useSSRContext` composable, then we use this to set the 404 status code
* @see src/entry-server.ts
* @see server.ts
*/
if (import.meta.env.SSR) {
const event = useSSRContext<VueroSSRContext>()?.event
if (event) {
setResponseHeader(event, 'Cache-Control', 'no-cache, no-store, must-revalidate')
setResponseStatus(event, 404)
}
}
</script>
<template>
<MinimalLayout>
<div class="error-container">
<div class="error-nav">
<VDarkmodeToggle />
</div>
<div class="error-wrapper">
<div class="error-inner has-text-centered">
<div class="bg-number">
404
</div>
<SVGErrorPlaceholder />
<h3>{{ t('pages.not-found.page-heading') }}</h3>
<p>
{{ t('pages.not-found.page-body') }}
</p>
<div class="button-wrap">
<VButton
color="primary"
elevated
to="/"
>
{{ t('pages.not-found.page-button') }}
</VButton>
</div>
</div>
</div>
</div>
</MinimalLayout>
</template>
<style lang="scss">
.error-container {
width: 100vw;
min-height: 100vh;
.error-nav {
.dark-mode {
position: absolute;
inset-inline-end: 0;
top: 0;
display: inline-block;
transform: scale(0.5);
}
}
.error-wrapper {
max-width: 840px;
margin: 0 auto;
padding-top: 40px;
.error-inner {
position: relative;
max-width: 540px;
margin: 0 auto;
.bg-number {
font-family: var(--font);
position: absolute;
top: -58px;
inset-inline-start: -50px;
inset-inline-end: 0;
margin: 0 auto;
font-size: 28rem;
font-weight: 600;
opacity: 0.15;
z-index: 0;
}
img,
svg,
h3,
p,
.button-wrap {
position: relative;
z-index: 1;
}
img,
.iconify {
display: block;
max-width: 100%;
margin: 0 auto;
}
h3 {
font-size: 1.5rem;
font-family: var(--font-alt);
color: var(--dark-text);
font-weight: 600;
margin-top: 10px;
}
p {
font-family: var(--font);
font-size: 1.1rem;
margin-bottom: 16px;
}
.button-wrap {
.button {
min-width: 220px;
min-height: 50px;
}
}
}
}
}
.is-dark {
.error-container {
.error-wrapper {
.error-inner {
.bg-number {
opacity: 0.09;
}
}
}
}
}
@media only screen and (width <= 767px) {
.error-container {
.error-wrapper {
padding-top: 60px;
.error-inner {
padding: 10px;
.bg-number {
top: -35px;
inset-inline-start: -18px;
inset-inline-end: 0;
font-size: 13rem;
}
img,
.iconify {
max-width: 345px;
}
}
}
}
}
</style>

30
src/pages/app.vue Normal file
View File

@@ -0,0 +1,30 @@
<script setup lang="ts">
import Navbar from '/src/layouts/navbar.vue'
definePage({
// all child pages will inherit this metadata
meta: {
requiresAuth: true,
},
})
const breadcrumb = [
{
label: '가격조사',
hideLabel: true,
// use external links
link: 'https://vuero.cssninja.io/',
},
{
label: '가격조사',
// or generate a router link with 'to' props
to: '/components/',
}
]
</script>
<template>
<Navbar>
<VBreadcrumb :items="breadcrumb" separator="succeeds" />
<RouterView />
</Navbar>
</template>

View File

@@ -0,0 +1,435 @@
<script setup lang="ts">
import axios from 'axios'
const registerFormOpen = ref(false)
const params = reactive({
cateCd: '',
contNo: '',
title: '',
compNm: '',
signDt: '',
contAmt: '',
contStatCd: '',
contStat: '',
regsabun: '',
regNm: '',
regDt: '',
reason: '',
page: 1,
row: 10,
flexColumn: [],
modalColumn: [],
})
params.modalColumn = [
{ key: 'cateNm', label: '분야' },
{ key: 'title', label: '제목' },
{ key: 'regNm', label: '담당자' },
{ key: 'stNm', label: '등록상태' },
{ key: 'title', label: '비고' },
{ key: 'regNm', label: '선택' },
]
const selectedCode = ref()
const data = reactive({
contractData: [],
priceSearchData: [],
})
const isLoading = ref(false)
watch(registerFormOpen, async (isOpen) => {
if (isOpen) {
isLoading.value = true
// error.value = null
try {
const priceSearchDataRespone = await axios.get('/api/cont/prcs')
console.log(priceSearchDataRespone.data)
data.priceSearchData = Array.isArray(priceSearchDataRespone.data) ? priceSearchDataRespone.data : []
}
catch (error) {
console.log(error)
data.priceData = []
}
}
})
function formatDate(dateStr) {
if (!dateStr) return ''
const date = new Date(dateStr)
return date.toLocaleDateString('ko-KR', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
}).replace(/\./g, '-').replace(/\s/g, '').replace(/-$/, '')
}
function getDateDiff(start, end) {
if (!start || !end) return null
const startDate = new Date(start)
const endDate = new Date(end)
const diff = Math.ceil((endDate - startDate) / (1000 * 60 * 60 * 24))
return diff >= 0 ? diff + 1 : null
}
const contractPeriod = computed(() => {
const start = params.regSdt
const end = params.regEdt
const startStr = formatDate(start)
const endStr = formatDate(end)
const diff = getDateDiff(start, end)
if (startStr && endStr && diff) {
return `${startStr} ~ ${endStr} (${diff}일)`
}
else if (startStr && endStr) {
return `${startStr} ~ ${endStr}`
}
else if (startStr) {
return `${startStr} ~`
}
else if (endStr) {
return `~ ${endStr}`
}
else {
return ''
}
})
</script>
<template>
<div class="page-content is-navbar-lg">
<div class="datatable-wrapper">
<div class="table-container">
<table class="table datatable-table is-fullwidth">
<colgroup>
<col style="width: 10%;">
<col style="width: 20%;">
<col style="width: 20%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 20%;">
<col style="width: 10%;">
</colgroup>
<tbody>
<tr>
<td>분야</td>
<td>
<span class="colum">
<VField class="pr-2">
<VCodeSelect
v-model="selectedCode"
cd_grp="5/"
/></VField>
</span>
</td>
<td>
<VButton
color="primary"
@click="registerFormOpen = true"
>
가격조사 가져오기
</VButton>
<VModal
is="form"
:open="registerFormOpen"
title="계약관리 등록"
size="contract-big"
actions="right"
@submit.prevent="registerFormOpen = false"
@close="registerFormOpen = false"
>
<template #content>
<div class="modal-form">
<ComVFlexTable
:data="data.priceSearchData"
:columns="params.modalColumn"
:compact="true"
:separators="true"
/>
</div>
</template>
<template #action>
<VButton
type="submit"
color="primary"
raised
>
Save Changes
</VButton>
</template>
</VModal>
</td>
<td>
<VField class="is-flex">
<VControl raw subcontrol>
<VCheckbox
label="가격조사여부"
color="info"
/>
</VControl>
</VField>
</td>
<td colspan="3">
<div class="column is-fullhd">
<VField>
<VControl>
<input
v-model="params.reason"
class="input custom-text-filter"
placeholder="가격조사 안했을 시 예외 사유 입력(필수)"
>
</VControl>
</VField>
</div>
</td>
</tr>
<tr>
<td>계약명</td>
<td colspan="6">
<VField>
<VControl>
<input
v-model="params.title"
class="input custom-text-filter"
placeholder="계약명"
>
</VControl>
</VField>
</td>
</tr>
<tr>
<td>계약상대자</td>
<td>
<VField>
<VControl>
<input
v-model="params.regNm"
class="input custom-text-filter"
placeholder="사업자번호"
>
</VControl>
</VField>
</td>
<td>
<VField>
<VControl>
<input
v-model="params.compNm"
class="input custom-text-filter"
placeholder="업체명"
>
</VControl>
</VField>
</td>
<td>
<VButton color="warning">
부정당 확인
</VButton>
</td>
<td>
<VButton color="success">
정상
</VButton>
</td>
<td>
<VButton color="warning">
분할계약 확인
</VButton>
</td>
<td>
<VButton color="success">
정상
</VButton>
</td>
</tr>
<tr>
<td>계약체결일</td>
<td>
<VField>
<VControl>
<input
v-model="params.signDt"
class="input custom-text-filter"
placeholder="계약체결일"
>
</VControl>
</VField>
</td>
<td>
<VButton
color="primary"
>
</VButton>
<VModal
actions="center"
title="계약금액"
>
<template #content>
<VPlaceholderSection
title="Go Premium"
subtitle="Unlock more features and business tools by going premium"
/>
</template>
<template #action>
<VButton color="primary" raised>
등록
</VButton>
</template>
</VModal>
</td>
<td colspan="2">
<VField>
<VControl>
<input
v-model="params.contAmt"
class="input custom-text-filter"
placeholder="금액"
>
</VControl>
</VField>
</td>
<td colspan="1">
<span class="colum">
<VField>
<VSelect>
<VOption value="">
수의계약 사유
</VOption>
</VSelect>
</VField>
</span>
</td>
<td>
<VButton>근거</VButton>
</td>
</tr>
<tr>
<td>계약기간</td>
<td colspan="1">
<div>
<div>
<VDatePicker
v-model="params.regSdt"
color="green"
trim-weeks
>
<template #default="{ inputValue, inputEvents }">
<VField>
<VControl icon="lucide:calendar">
<input
class="input v-input"
type="text"
:value="inputValue"
placeholder="시작일"
v-on="inputEvents"
>
</VControl>
</VField>
</template>
</VDatePicker>
</div>
</div>
</td>
<td colspan="1">
<div class="pr-2">
<div>
<VDatePicker
v-model="params.regEdt"
color="green"
trim-weeks
>
<template #default="{ inputValue, inputEvents }">
<VField>
<VControl icon="lucide:calendar">
<input
class="input v-input"
type="text"
:value="inputValue"
placeholder="종료일"
v-on="inputEvents"
>
</VControl>
</VField>
</template>
</VDatePicker>
</div>
</div>
</td>
<td colspan="5">
계약기간 : {{ contractPeriod }}
</td>
</tr>
<tr>
<td>첨부파일</td>
<td colspan="1">
<VButton color="info">
등록
</VButton>
</td>
<td colspan="5" />
</tr>
</tbody>
</table>
<div class="bottom-button">
<VButton
to="/app/DocumentManagement"
>
(임시)
</VButton>
<VButton> </VButton>
<VButton> </VButton>
</div>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
.table tbody td {
color: var(--smoke-white);
}
.datatable-table {
padding: 12px 12px;
td:nth-child(1) {
background-color: var(--primary);
text-align: center;
}
tr:nth-child(3) {
text-align: center;
}
tr td button{
width: 100%;
}
tr:nth-child(5) > td:nth-child(4) {
color: black;
}
}
.bottom-button {
text-align: center;
button {
background-color: cornflowerblue;
margin: 10px;
font-weight: bold;
border-color: var(--primary);
color: white;
}
}
button:nth-child(2) {
background-color: #AB9A6c;
}
button:nth-child(3) {
background-color: silver;
}
.field {
margin-bottom: 0px;
}
</style>

View File

@@ -0,0 +1,389 @@
<script setup lang="ts">
const affiliationCode = ref()
const taxCode = ref('')
const currencyCode = ref('KRW')
const taxInvoiceCode = ref()
const accountSubjectCode = ref()
const params = reactive({
cateCd: '',
contNo: '',
title: '',
compNm: '',
signDt: '',
contAmt: 0,
contStatCd: '',
contStat: '',
regsabun: '',
regNm: '',
regDt: '',
reason: '',
page: 1,
row: 10,
flexColumn: [],
modalColumn: [],
})
const selected = ref('cost')
const formattedNumber = ref(0)
function onInput(event) {
let onlyNumber = event.target.value.replace(/[^0-9]/g, '')
formattedNumber.value = onlyNumber ? Number(onlyNumber).toLocaleString() : ''
}
</script>
<template>
<div class="page-content is-navbar-lg">
<div class="datatable-wrapper">
<div class="table-container">
<table class="table datatable-table is-fullwidth">
<colgroup>
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
</colgroup>
<tbody>
<tr>
<td>소속</td>
<td colspan="2">
<VField>
<VCodeSelect
v-model="affiliationCode"
cd_grp=11 >
</VCodeSelect>
</VField>
</td>
<td colspan="7" />
</tr>
<tr>
<td>계약명</td>
<td colspan="9">
<VField>
<VControl>
<input
v-model="params.title"
class="input custom-text-filter"
placeholder="계약명"
>
</VControl>
</VField>
</td>
</tr>
<tr>
<td>계약상대자</td>
<td colspan="2">
<VField>
<VControl>
<input
v-model="params.regNm"
class="input custom-text-filter"
placeholder="사업자번호"
>
</VControl>
</VField>
</td>
<td colspan="2">
<VField>
<VControl>
<input
v-model="params.title"
class="input custom-text-filter"
placeholder="업체명"
>
</VControl>
</VField>
</td>
<td>
<VButton color="danger">
구매처 확인
</VButton>
</td>
<td>
<VButton color="success">
정상
</VButton>
</td>
<td colspan="2" />
</tr>
<tr>
<td>공급가액</td>
<td colspan="2">
<VField>
<VControl>
<input
:value="formattedNumber"
placeholder="금액"
@input="onInput"
class="input custom-text-filter"
>
</VControl>
</VField>
</td>
<td style="color: black">(부가세 별도)</td>
<td style="background-color: var(--primary); text-align: center">
<span>세금코드</span>
</td>
<td colspan="2">
<VField>
<VCodeSelect v-model="taxCode" cd_grp="12">
<template #code="{ item }">
{{ item.cd }}
</template>
</VCodeSelect>
</VField>
</td>
<td style="background-color: var(--primary); text-align: center">통화</td>
<td colspan="2">
<VField>
<VCodeSelect v-model="currencyCode" cd_grp="13">
<template #code="{ item }">
{{ item.cd }}
</template>
</VCodeSelect>
</VField>
</td>
</tr>
<tr>
<td>증빙일</td>
<td colspan="2">
<div>
<VDatePicker
v-model="params.regSdt"
color="green"
trim-weeks
>
<template #default="{ inputValue, inputEvents }">
<VField>
<VControl icon="lucide:calendar">
<input
class="input v-input"
type="text"
:value="inputValue"
placeholder="증빙일"
v-on="inputEvents"
>
</VControl>
</VField>
</template>
</VDatePicker>
</div>
</td>
<td />
<td style="background-color: var(--primary); text-align: center">
<span>전기일</span>
</td>
<td colspan="2">
<div>
<VDatePicker
v-model="params.regSdt2"
color="green"
trim-weeks
>
<template #default="{ inputValue, inputEvents }">
<VField>
<VControl icon="lucide:calendar">
<input
class="input v-input"
type="text"
:value="inputValue"
placeholder="전기일"
v-on="inputEvents"
>
</VControl>
</VField>
</template>
</VDatePicker>
</div>
</td>
</tr>
<tr>
<td>계좌관리</td>
<td>
<VField>
<VControl>
<input
v-model="params.title"
class="input custom-text-filter"
placeholder="계약번호"
>
</VControl>
</VField>
</td>
<td colspan="2">
<VField>
<VControl>
<input
v-model="params.title"
class="input custom-text-filter"
placeholder="계좌번호"
>
</VControl>
</VField>
</td>
<td>
<VButton color="info">
계좌조회
</VButton>
</td>
<td >
<VButton color="warning">
계좌확인중
</VButton>
</td>
<td>
<VButton color="success">
정상
</VButton>
</td>
<td style="background-color: var(--primary); text-align: center">
<span>세금계산서</span>
</td>
<td colspan="2">
<VField>
<VCodeSelect
v-model="taxInvoiceCode"
cd_grp=12 >
</VCodeSelect>
</VField>
</td>
</tr>
<tr>
<td>예산관리</td>
<td>
<VButton
color="primary"
@click="centeredActionsOpen = true"
>
G/L계정
</VButton>
</td>
<td colspan="2">
<VField>
<VCodeSelect
v-model="accountSubjectCode"
cd_grp=12 >
</VCodeSelect>
</VField>
</td>
<td>
<VButton
color="info"
@click="selected = 'cost'"
:class="{ 'disabled-button': selected === 'wbs' }"
>
코스트센터
</VButton>
</td>
<td>
<VButton
color="info"
@click="selected = 'wbs'"
:class="{ 'disabled-button': selected === 'cost' }"
>
WBS
</VButton>
</td>
<td colspan="2">
<VField>
<VControl>
<input
v-model="params.title"
class="input custom-text-filter"
placeholder="코드"
>
</VControl>
</VField>
</td>
</tr>
<tr>
<td>첨부파일</td>
<td colspan="1">
<VButton color="info">
등록
</VButton>
</td>
<td colspan="5">
<div>('준공보고서' 또는 '검수보고서' )</div>
</td>
</tr>
</tbody>
</table>
<div class="bottom-button">
<VButton> </VButton>
<VButton> </VButton>
</div>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
.table tbody td {
color: var(--smoke-white);
}
.datatable-table {
td{
font-family: var(--font),serif;
vertical-align: middle;
padding: 4px 20px;
border-bottom: 1px solid var(--fade-grey);
}
td:nth-child(1) {
background-color: var(--primary);
text-align: center;
}
tr:nth-child(3) {
text-align: center;
}
tr td button{
width: 100%;
}
}
.bottom-button {
text-align: center;
button {
background-color: cornflowerblue;
margin: 10px;
font-weight: bold;
border-color: var(--primary);
color: white;
}
}
button:nth-child(2) {
background-color: #AB9A6c;
}
button:nth-child(3) {
background-color: silver;
}
.field {
margin: 0px 0px;
}
.button.v-button {
padding: 0px 0px;
}
.disabled-button {
//opacity: 0.5;
background-color: #ccc;
cursor: not-allowed;
//transition: all 0.2s;
}
</style>

View File

@@ -0,0 +1,521 @@
<script setup lang="ts">
import { savePrice } from '/src/service/priceApi'
import { Person } from '/@src/utils/types'
const notyf = useNotyf()
const showTable = ref(false)
const detailActionsOpen = ref(false)
const apprLine = defineModel<Person[]>()
const today = new Date()
const loading = ref(false)
//포커스 설정 변수 정리
const cateSelectRef = ref()
const titleRef = ref()
const contentRef = ref()
const prcsBizsRef = ref()
const generalParams = reactive({
title: "",
content: "",
regSdat: "",
regEdat: "",
prvYn: true,
prvRsn : "",
prvPwd : "",
aiYn: true,
})
const params = reactive({
cateSelect: '',
prcsAttsColumn:[ //첨부파일 입력
{ key: 'logiFnm', label: '구분'},
{ key: 'data', label: '데이터'}
],
prcsAtts: [], //첨부파일 데이터
felxColumn: [
{ key: 'gubunCd', label: '구분'},
{ key: 'deptNm', label: '부서' },
{ key: 'sabun', label: '사번' },
{ key: 'name', label: '이름' },
{ key: 'attendCd', label: '비고' },
{ key: 'actions', label: '동작'}
],
priceData:[],
prcsBizsColumn: [ //견적사 입력
{ key: 'num', label: '구분', width: '10%' },
{ key: 'email', label: '이메일', editable: true, width: '50px' },
{ key: 'bizNo', label: '사업자번호', editable: true, width: '50px'},
{ key: 'actions', label: '동작', width: '100px'}
],
prcsBizs: [], //견적사 입력 데이터
dtlSpecsColumn: [
{ key: 'num', label: '번호', editable: false },
{ key: 'itemNm', label: '품명', editable: true },
{ key: 'spec', label: '규격', editable: true },
{ key: 'unit', label: '단위', editable: true },
{ key: 'qty', label: '수량', editable: true },
{ key: '', label: '단가(VAT별도)', editable: false },
{ key: '', label: '금액(VAT별도)', editable: false },
{ key: '', label: '삭제', editable: false },
],
dtlSpecs: [], //상세 규격 입력 데이터
})
const validation = () => {
if(!params.cateSelect){
notyf.error("필수값 입니다.")
cateSelectRef.value.focus() //todo 선택박스 필수값 체크하기
console.log(params.cateSelect)
return false
}
if(!generalParams.title){
notyf.error("필수값 입니다.")
titleRef.value.focus()
return false
}
if(!generalParams.content){
notyf.error("필수값 입니다.")
contentRef.value.focus()
return false
}
if(params.prcsBizs==[]){
notyf.error("견적요청을 입력해주세요")
prcsBizsRef.value.focus()
return false
}
if(apprLine.value.length < 2){
notyf.error("결재선은 두 명이상 입력해주세요.")
return false
}
return true
}
const savePriceOne = async () => {
let res = null
try{
loading.value = true
// if (!validation()) {
// return;
// }
const paramsPrice ={
cateCd : params.cateSelect,
title: generalParams.title,
content: generalParams.content,
regSdat: formatDate(generalParams.regSdat),
regEdat: formatDate(generalParams.regEdat),
prvYn: true,
prvRsn : "",
prvPwd : "",
aiYn: true,
prcsBizs: params.prcsBizs.map(({ num, ...rest }) => rest), //견적사 입력 데이터
dtlSpecs: params.dtlSpecs.map(({ num, ...rest }) => rest), //상세 규격 입력 데이터
prcsAtts: params.prcsAtts, //첨부파일 데이터
apprReqs: apprLine.value.map(({ deptNm, ...rest }) => rest), //결재선 데이터
}
res = await savePrice(paramsPrice)
if(res.request.status == '200'){
notyf.primary('수정 되었습니다.')
router.push({path: '/app/priceManagement'})
}
}catch(e){
notyf.error(e.message)
}finally {
loading.value = false
}
}
const addNewEstimateRow = () => {
const newRow = {}
params.prcsBizsColumn.forEach(col => {
if (col.key && col.key !== 'actions') {
newRow[col.key] = ''
}
})
params.prcsBizs.push(newRow)
}
const onDelete = (index: number) => {
if(params.prcsBizs.length-1 !== params.prcsBizsColumn.length
|| (params.prcsBizsColumn.length == 4 && params.prcsBizs.length-1 == params.prcsBizsColumn.length))
{
params.prcsBizs.splice(index, 1)
}
}
const addNewDetailRow = () => {
const newRow = {}
params.dtlSpecsColumn.forEach(col => {
if (col.key) {
newRow[col.key] = ''
}
})
params.dtlSpecs.push(newRow)
console.log(params.dtlSpecs.length)
}
const onDetailDelete = (index: number) => {
if(params.dtlSpecs.length-1 !== params.dtlSpecsColumn.length
|| (params.dtlSpecsColumn.length == 8 && params.dtlSpecs.length-1 == params.dtlSpecsColumn.length))
{
params.dtlSpecs.splice(index, 1)
}
}
function formatDate(dateStr) {
if (!dateStr) return ''
const date = new Date(dateStr)
return date.toLocaleDateString('ko-KR', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
}).replace(/\./g, '-').replace(/\s/g, '').replace(/-$/,'')
}
const onPayDelete = (index: number) => {
if(params.dtlSpecs.length-1 !== params.dtlSpecsColumn.length
|| (params.dtlSpecsColumn.length == 8 && params.dtlSpecs.length-1 == params.dtlSpecsColumn.length))
{
params.dtlSpecs.splice(index, 1)
}
}
const fileInput = ref<HTMLInputElement | null>(null)
const fileName = ref('')
function openFileDialog() {
fileInput.value?.click()
}
function onFileChange(event: Event) {
const target = event.target as HTMLInputElement
if (target.files && target.files.length > 0) {
fileName.value = target.files[0].name
// 여기서 파일 업로드 로직을 추가할 수 있습니다.
// 예: emit('file-selected', target.files[0])
}
}
</script>
<template>
<div class="page-content is-navbar-lg">
<div class="datatable-wrapper">
<div class="table-container">
<table class="table datatable-table is-fullwidth">
<colgroup>
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
</colgroup>
<tbody>
<tr>
<td>분야</td>
<td colspan="2">
<span class="column">
<VField class="pr-2">
<VCodeSelect
cd_grp=5
v-model="params.cateSelect"
ref="cateSelectRef"/>
</VField>
</span>
</td>
<td>제목</td>
<td colspan="6">
<div class="column is-fullhd">
<VField class="pr-2">
<VControl>
<input
v-model="generalParams.title"
class="input custom-text-filter"
placeholder="제목"
ref="titleRef"
>
</VControl>
</VField>
</div>
</td>
</tr>
<tr>
<td>내용</td>
<td colspan="9">
<div class="column is-fullhd">
<VField class="pr-2">
<VControl>
<textarea
v-model="generalParams.content"
class="input custom-text-filter"
placeholder="내용"
ref="contentRef"
/>
</VControl>
</VField>
</div>
</td>
</tr>
<tr>
<td>견적요청</td>
<td colspan="9">
<VButton
color="primary"
icon="fas fa-plus"
elevated
@click="showTable = !showTable"
style="width: 19%;"
>
견적사 입력
</VButton>
<div v-if="showTable" class="mt-2">
<ComVFlexTable
:key="params.prcsBizs.length"
:data="params.prcsBizs"
:columns="params.prcsBizsColumn"
:compact="true"
:separators="true"
:clickable="true"
>
<template #body-cell="{ row, column, index, value }">
<div>
<!-- 다른 editable 컬럼은 input -->
<input
v-if="column.editable"
v-model="row[column.key]"
class="editable-input"
ref="prcsBizsRef"
/>
<span v-else-if="column.key=='num'">{{index + 1}}</span>
<!-- readonly 출력 -->
<span v-else class="lnil lnil-close"
@click="onDelete(index)">{{ value }}</span>
</div>
</template>
</ComVFlexTable>
<div class="mt-2">
<VButton
color="primary"
icon="fas fa-plus"
@click="addNewEstimateRow"
>
추가
</VButton>
</div>
</div>
</td>
</tr>
<tr>
<td>규격입력</td>
<td colspan="2">
<VButton
color="primary"
icon="fas fa-plus"
elevated
@click="detailActionsOpen = true"
style="width: 100%;"
>
상세 규격 입력
</VButton>
<VModal
:open="detailActionsOpen"
actions="center"
title="상세 규격 입력"
size="contract-big"
@close="detailActionsOpen = false"
>
<template #content>
<VButton color="success">일괄업로드</VButton>
<ComVFlexTable
:key="params.dtlSpecs.length"
:data="params.dtlSpecs"
:columns="params.dtlSpecsColumn"
:separators="true"
:clickable="true"
>
<template #body-cell="{ row, column, index, value }">
<!-- : 특정 컬럼이면 input, 아니면 그냥 출력 -->
<div>
<input
v-if="column.editable"
v-model="row[column.key]"
class="editable-input"
/>
<span v-else-if="column.key=='num'">{{index+1}}</span>
<span v-else class="lnil lnil-close"
@click="onDetailDelete(index)">{{ value }}</span>
</div>
</template>
</ComVFlexTable>
<div class="mt-2">
<VButton
color="primary"
icon="fas fa-plus"
@click="addNewDetailRow"
>
추가
</VButton>
</div>
</template>
<template #action>
<VButton color="primary">
등록
</VButton>
</template>
</VModal>
</td>
<td>등록기간</td>
<td colspan="5">
<div class="columns">
<div class="column is-5">
<VDatePicker
v-model="generalParams.regSdat"
color="green"
trim-weeks
>
<template #default="{ inputValue, inputEvents }">
<VField>
<VControl icon="lucide:calendar">
<input
class="input v-input"
type="text"
:value="inputValue"
v-on="inputEvents"
placeholder="시작일"
>
</VControl>
</VField>
</template>
</VDatePicker>
</div>
<div style="transform: translateY(15px)">~</div>
<div class="column is-5">
<VDatePicker
v-model="generalParams.regEdat"
color="green"
trim-weeks
:minDate="today"
>
<template #default="{ inputValue, inputEvents }">
<VField>
<VControl icon="lucide:calendar">
<input
class="input v-input"
type="text"
:value="inputValue"
v-on="inputEvents"
placeholder="종료일"
>
</VControl>
</VField>
</template>
</VDatePicker>
</div>
</div>
</td>
</tr>
<tr>
<td>첨부파일</td>
<td colspan="10">
<VField class="file has-name is-left">
<VButton @click="openFileDialog">
파일 첨부
</VButton>
<input
ref="fileInput"
type="file"
style="display: none"
@change="onFileChange"
/>
<span v-if="fileName" class="file-name">{{ fileName }}</span>
</VField>
</td>
</tr>
</tbody>
</table>
</div>
<div class="column is-12">
<VUser v-model="apprLine"/>
</div>
<div class="column is-12">
<VField class="pr-2">
<VLabel class="has-fullwidth">
결재선
</VLabel>
</VField>
<ComVFlexTable
:data="apprLine"
:columns="params.felxColumn"
:separators="true"
:clickable="true"
:compact="true">
<template #body-cell="{ row, column, index, value }">
<!-- : 특정 컬럼이면 input, 아니면 그냥 출력 -->
<div>
<!-- <span v-if="column.key=='gubunCd' && index == 0" class="column">-->
<!-- {{row.gubunNm}}-->
<!-- </span>-->
<span v-if="column.key=='gubunCd'" class="column">
<VField class="pr-1">
<VCodeSelect
cd_grp=7
v-model="row.gubunCd"
/>
<!-- :disabled="index === 0"-->
</VField>
</span>
<span v-else-if="column.key=='attendCd' && index != 0" class="column">
<VField class="pr-1">
<VCodeSelect
placeholder="재중"
cd_grp=6
v-model="row.attendCd"/>
</VField>
</span>
<span v-else
@click="onDetailDelete(index)">{{value}}</span>
</div>
</template>
</ComVFlexTable>
</div>
<VButton
color="primary"
@click.stop="savePriceOne"
raised>
등록
</VButton>
</div>
</div>
</template>
<style scoped lang="scss">
.table tbody td {
color: var(--smoke-white);
}
.datatable-table {
padding: 12px 12px;
td:nth-child(1) {
background-color: var(--primary);
}
td:nth-child(3) {
background-color: var(--primary);
}
}
</style>

187
src/pages/app/approval.vue Normal file
View File

@@ -0,0 +1,187 @@
<script setup lang="ts">
import { getPriceList } from '/@src/service/price'
onBeforeMount(async () => {
await getPriceListData()
})
const selectedCode = ref()
const paymentCode = ref()
const emits = defineEmits(['on-tr-click'])
const masks = ref({
modelValue: 'YYYY-MM-DD',
})
const params = reactive({
priceData: [],
felxColumn: [
{ key: 'cateNm', label: '분야' },
{ key: 'title', label: '제목' },
{ key: 'regNm', label: '담당자' },
{ key: 'regSdat', label: '최초등록일' },
{ key: 'stNm', label: '등록 상태' },
{ key: 'title', label: '비고' }
],
prcsParams: []
})
async function getPriceListData(){
params.prcsParams = {
params:{
regSdt: '1970-01-01',
regEdt: '2070-01-01',
page: '1',
row: '3'
}
}
const result = await getPriceList(params.prcsParams)
params.priceData = result.content
}
const searchPrice = () => {
}
</script>
<template>
<div class="page-content is-navbar-lg">
<div>
<div class="datatable-toolbar">
<div class="column is-2">
<VField class="pr-2">
<VLabel class="has-fullwidth">
분야
</VLabel>
<VCodeSelect
cd_grp=5
v-model="selectedCode"/>
</VField>
</div>
<div class="column is-2">
<VField class="pr-2">
<VLabel class="has-fullwidth">
결제상태
</VLabel>
<VCodeSelect
cd_grp=2
v-model="paymentCode"/>
</VField>
</div>
<div class="column is-2">
<VField class="pr-2">
<VLabel class="has-fullwidth">
담당자
</VLabel>
<VControl>
<input
v-model="params.title"
class="input custom-text-filter"
placeholder="담당자"
>
</VControl>
</VField>
</div>
<div class="column is-5">
<VField class="pr-2">
<VLabel class="has-fullwidth">
등록기간
</VLabel>
<VControl>
<div class="columns">
<div class="column is-6">
<VDatePicker
v-model.string="params.regSdt"
color="green"
:masks="masks"
trim-weeks
>
<template #default="{ inputValue, inputEvents }">
<VField>
<VControl icon="lucide:calendar">
<input
class="input v-input"
type="text"
:value="inputValue"
v-on="inputEvents"
placeholder="시작일"
>
</VControl>
</VField>
</template>
</VDatePicker>
</div>
<div style="transform: translateY(15px)">~</div>
<div class="column is-6">
<VDatePicker
v-model.string="params.regEdt"
color="green"
:masks="masks"
trim-weeks
>
<template #default="{ inputValue, inputEvents }">
<VField>
<VControl icon="lucide:calendar">
<input
class="input v-input"
type="text"
:value="inputValue"
v-on="inputEvents"
placeholder="종료일"
>
</VControl>
</VField>
</template>
</VDatePicker>
</div>
</div>
</VControl>
</VField>
</div>
<div class="column is-1">
<div style="padding-top:20px;float:right;">
<VButtons>
<VButton
color="primary"
elevated
icon="fas fa-search"
@click.stop="searchPrice"
>
검색
</VButton>
</VButtons>
</div>
</div>
</div>
<div class="datatable-wrapper">
<ComVFlexTable
:data="params.priceData"
:columns="params.felxColumn"
:separators="true"
:clickable="true"
:compact="true"
/>
</div>
</div>
<VButtons class="v-buttons-right-align">
<VButton
color="primary"
icon="fas fa-plus"
elevated
to="/app/PriceInsert"
>
등록
</VButton>
</VButtons>
</div>
</template>
<style scoped lang="scss">
.v-buttons-right-align {
display: flex;
justify-content: flex-end;
margin-top: 1rem; // 필요 시 간격 추가
}
</style>

View File

@@ -0,0 +1,255 @@
<script setup lang="ts">
import axios from 'axios'
import {useRouter} from "vue-router";
export type MinimalTheme = 'darker' | 'light'
const emits = defineEmits(['on-search', 'on-tr-click'])
const selUser = ref()
const masks = ref({
modelValue: 'YYYY-MM-DD',
})
const selectedCode = ref()
watch(selUser, (value) => {
console.log(value)
})
const data = reactive({
contractData: [],
priceData: [],
})
onMounted(async () => {
try {
const contractResponse = await axios.get('/api/cont/prcs')
data.contractData = Array.isArray(contractResponse.data) ? contractResponse.data : []
}
catch (error) {
console.log(error)
data.contractData = []
}
})
const registerFormOpen = ref(false)
const isLoading = ref(false)
watch(registerFormOpen, async (isOpen) => {
if (isOpen) {
isLoading.value = true
// error.value = null
try {
const priceResponse = await axios.get('/api/prcs/page?regSdt=1970-01-01&regEdt=2070-01-01&page=1&row=10')
console.log(priceResponse.data.content)
data.priceData = Array.isArray(priceResponse.data.content) ? priceResponse.data.content : []
}
catch (error) {
console.log(error)
data.priceData = []
}
}
})
const router = useRouter()
function handleRowClick(row) {
router.push({
name: 'DocumentManagement',
params: { id: row.contractDetailedData },
})
}
const params = reactive({
priceData: [],
flexColumn: [
{ key: 'cateNm', label: '분야' },
{ key: 'title', label: '제목' },
{ key: 'regNm', label: '담당자' },
{ key: 'regSdat', label: '최초등록일' },
{ key: 'stNm', label: '등록 상태' },
{ key: 'title', label: '비고' },
],
prcsParams: [],
})
</script>
<template>
<div class="page-content is-navbar-lg">
<div>
<div class="datatable-toolbar">
<div class="column is-2">
<!-- <VUserList v-model="selUser"/>-->
<VField class="pr-2">
<VLabel class="has-fullwidth">
분야
</VLabel>
<VCodeSelect
cd_grp=5
v-model="selectedCode"/>
</VField>
</div>
<div class="column is-2">
<!-- <VUserList v-model="selUser"/>-->
<VField class="pr-2">
<VLabel class="has-fullwidth">
계약번호
</VLabel>
<VControl>
<input
v-model="params.title"
class="input custom-text-filter"
placeholder="계약번호"
>
</VControl>
</VField>
</div>
<div class="column is-2">
<VField class="pr-2">
<VLabel class="has-fullwidth">
담당자
</VLabel>
<VControl>
<input
v-model="params.title"
class="input custom-text-filter"
placeholder="담당자명"
>
</VControl>
</VField>
</div>
<div class="column is-5">
<VField class="pr-2">
<VLabel class="has-fullwidth">
계약일
</VLabel>
<VControl>
<div class="columns">
<div class="column is-6">
<VDatePicker
v-model="params.regSdt"
color="green"
:masks="masks"
trim-weeks
>
<template #default="{ inputValue, inputEvents }">
<VField>
<VControl icon="lucide:calendar">
<input
class="input v-input"
type="text"
:value="inputValue"
placeholder="시작일"
v-on="inputEvents"
>
</VControl>
</VField>
</template>
</VDatePicker>
</div>
<div style="transform: translateY(15px)">
~
</div>
<div class="column is-6">
<VDatePicker
v-model="params.regEdt"
color="green"
:masks="masks"
trim-weeks
>
<template #default="{ inputValue, inputEvents }">
<VField>
<VControl icon="lucide:calendar">
<input
class="input v-input"
type="text"
:value="inputValue"
placeholder="종료일"
v-on="inputEvents"
>
</VControl>
</VField>
</template>
</VDatePicker>
</div>
</div>
</VControl>
</VField>
</div>
<div class="column is-1" style="position:relative;">
<div style="padding-top:20px;float:right;">
<VButtons>
<VButton
color="primary"
elevated
icon="fas fa-search"
@click.stop="emits('on-search', params)"
>
검색
</VButton>
</VButtons>
</div>
</div>
</div>
<div class="datatable-wrapper">
<ComVFlexTable
:data="data.contractData"
:columns="params.flexColumn"
:separators="true"
:clickable="true"
:rounded="true"
:compact="true"
/>
</div>
</div>
<VButtons>
<VButton
color="primary"
icon="fas fa-plus"
elevated
to="/app/ContractInsert"
style="margin-top: 20px"
>
등록
</VButton>
</VButtons>
<!-- 모달창 시작-->
<VModal
is="form"
:open="registerFormOpen"
title="계약관리 등록"
size="contract-big"
actions="right"
@submit.prevent="registerFormOpen = false"
@close="registerFormOpen = false"
>
<template #content>
<div class="modal-form">
<ComVFlexTable
:data="data.priceData"
:columns="{ cateNm: '분야',
title: '제목',
content: '담당자',
stNm: '등록상태',
stCd: '비고',
regSabun: '선택',
}"
:compact="true"
:separators="true"
@row-click="handleRowClick"
/>
</div>
</template>
<template #action>
<VButton
type="submit"
color="primary"
raised
>
Save Changes
</VButton>
</template>
</VModal>
</div>>
</template>

5
src/pages/app/index.vue Normal file
View File

@@ -0,0 +1,5 @@
<template>
<div>
<!-- Your content here -->
</div>
</template>

View File

@@ -0,0 +1,481 @@
<script setup lang="ts">
// import { getIntegratedApproval } from '/src/service/integratedApprovalApi'
import { getIntegratedApproval, updateIntegratedApproval } from '/src/service/integratedPayment'
import type { VFlexTableWrapperSortFunction, VFlexTableWrapperFilterFunction } from '/src/components/app-vuero/ComVFlexTableWrapper.vue'
import { users } from '/src/data/layouts/card-grid-v1'
onBeforeMount(async () => {
await getIntegratedPaymentList()
})
const isModalOpen = ref(false)
const selectedRow = ref<any>(null)
const masks = ref({
modelValue: 'YYYY-MM-DD',
})
const params = reactive({
title: '',
priceData: [],
flexColumn: [
{ key: 'apprNo', label: '결재번호', cellClass: 'paymentColumn1' },
{ key: 'title', label: '제목', cellClass: 'paymentColumn2' },
{ key: 'name', label: '작성자', cellClass: 'paymentColumn3' },
{
key: 'regDt',
label: '등록일',
cellClass: 'paymentColumn4',
format: formatRegDt,
},
{ key: 'process', label: '구분', cellClass: 'paymentColumn5' },
],
flexWrapperColumn: {
apprNo: { label: '결재번호', cellClass: 'paymentColumn1', searchable: true, sortable: true },
title: { label: '제목', cellClass: 'paymentColumn2', searchable: true, sortable: true },
name: { label: '작성자', cellClass: 'paymentColumn3', searchable: true, sortable: true },
regDt: { label: '등록일', cellClass: 'paymentColumn4', searchable: true, sortable: true },
process: { label: '구분', cellClass: 'paymentColumn5', searchable: true, sortable: true },
},
paymentParams: [],
})
function formatRegDt(value) {
return value ? value.substring(0, 16) : ''
}
async function getIntegratedPaymentList() {
const paymentParams = {
title: '',
page: '1',
row: '10',
sabun: '17131303',
}
const result = await getIntegratedApproval(paymentParams)
params.paymentParams = result.content.map(item => ({
...item,
process: gubunMap[item.gubun] || '',
}))
}
async function updateIntegratedPaymentList() {
const paymentUpdateParams = {
apprNo: selectedRow.value.apprNo,
apprOrd: selectedRow.value.apprOrd,
apprStatCd: '0200', // 결재 상태변경 (결재승인 0200 결재회수 0300 결재반려 0400)
reason: '',
sabun: selectedRow.value.sabun,
}
const result = await updateIntegratedApproval(paymentUpdateParams)
console.log(result)
alert('결재승인완료')
window.location.reload(true)
}
const gubunMap = {
SAP: '전표생성',
SVCM: '가격조사',
}
const searchApproval = async () => {
const paymentParams = {
params: {
title: params.title,
page: '1',
row: '10',
},
}
const result = await getIntegratedApproval(paymentParams)
params.paymentParams = result.content
console.log(params.paymentParams)
}
type User = (typeof users)[0]
// duplicate user data to grow the array
const data: User[] = []
for (let i = 0; i < 1000; i++) {
data.push(...users)
}
// this is a sample for custom sort function
const locationSorter: VFlexTableWrapperSortFunction<User> = ({ order, a, b }) => {
if (order === 'asc') {
return a.location.localeCompare(b.location)
}
else if (order === 'desc') {
return b.location.localeCompare(a.location)
}
return 0
}
// this is a sample for custom filter function
const userFilter: VFlexTableWrapperFilterFunction<User> = ({ searchTerm, row }) => {
if (!searchTerm) {
return true
}
// search either in the name or the bio
return (
row.name.toLocaleLowerCase().includes(searchTerm.toLocaleLowerCase())
|| row.bio.toLocaleLowerCase().includes(searchTerm.toLocaleLowerCase())
)
}
// 로우 클릭 핸들러
const onRowClick = (row: any) => {
selectedRow.value = row
isModalOpen.value = true
console.log(selectedRow)
}
</script>
<template>
<div class="page-content is-navbar-lg">
<div>
<div class="datatable-toolbar">
<div class="column is-2">
<VField class="pr-2">
<VLabel class="has-fullwidth">
결재제목
</VLabel>
<VControl>
<input
v-model="params.title"
class="input custom-text-filter"
placeholder="결재제목"
>
</VControl>
</VField>
</div>
<div class="column is-5">
<VField class="pr-2">
<VLabel class="has-fullwidth">
등록기간
</VLabel>
<VControl>
<div class="columns">
<div class="column is-6">
<VDatePicker
v-model.string="params.regSdt"
color="green"
:masks="masks"
trim-weeks
>
<template #default="{ inputValue, inputEvents }">
<VField>
<VControl icon="lucide:calendar">
<input
class="input v-input"
type="text"
:value="inputValue"
placeholder="시작일"
v-on="inputEvents"
>
</VControl>
</VField>
</template>
</VDatePicker>
</div>
<div style="transform: translateY(15px)">
~
</div>
<div class="column is-6">
<VDatePicker
v-model.string="params.regEdt"
color="green"
:masks="masks"
trim-weeks
>
<template #default="{ inputValue, inputEvents }">
<VField>
<VControl icon="lucide:calendar">
<input
class="input v-input"
type="text"
:value="inputValue"
placeholder="종료일"
v-on="inputEvents"
>
</VControl>
</VField>
</template>
</VDatePicker>
</div>
</div>
</VControl>
</VField>
</div>
<div class="column is-1">
<div style="padding-top:20px;float:right;">
<VButtons>
<VButton
color="primary"
elevated
icon="fas fa-search"
@click.stop="searchApproval"
>
검색
</VButton>
</VButtons>
</div>
</div>
</div>
<div class="datatable-wrapper">
<ComVFlexTable
:data="params.paymentParams"
:columns="params.flexColumn"
:separators="true"
:clickable="true"
:compact="true"
:use-payment-header="true"
@row-click="onRowClick"
>
<template #payment-header>
<div class="flex-table-header">
<span class="flex-table-item paymentColumn1">결재번호</span>
<span class="flex-table-item paymentColumn2">제목</span>
<span class="flex-table-item paymentColumn3">작성자</span>
<span class="flex-table-item paymentColumn4">등록일</span>
<span class="flex-table-item paymentColumn5">구분</span>
</div>
</template>
</ComVFlexTable>
<!-- 모달 컴포넌트 추가 -->
<VModal
is="form"
v-model:open="isModalOpen"
title="결재창"
size="big"
actions="center"
@submit.prevent="isModalOpen = false"
@close="isModalOpen = false"
>
<template #content>
<div class="modal-form">
<div class="columns is-multiline">
<div class="column is-4">
<div class="field">
<label>결재번호</label>
<div class="control">
<input
type="text"
class="input"
placeholder="결재번호"
:value="selectedRow?.apprNo"
readonly
>
</div>
</div>
</div>
<div class="column is-4">
<div class="field">
<label>제목</label>
<div class="control">
<input
type="text"
class="input"
placeholder="제목"
:value="selectedRow?.title"
readonly
>
</div>
</div>
</div>
<div class="column is-4">
<div class="field">
<label>작성자</label>
<div class="control">
<input
type="text"
class="input"
placeholder="작성자"
:value="selectedRow?.name"
readonly
>
</div>
</div>
</div>
<div class="column is-4">
<div class="field">
<label>구분</label>
<div class="control">
<input
type="text"
class="input"
placeholder="구분"
:value="selectedRow?.process"
readonly
>
</div>
</div>
</div>
<div class="column is-4">
<div class="field">
<label>결재상태</label>
<div class="control">
<input
type="text"
class="input"
placeholder="결재상태"
:value="selectedRow?.apprStat"
readonly
>
</div>
</div>
</div>
<div class="column is-4">
<div class="field">
<label>등록일</label>
<div class="control">
<input
type="text"
class="input"
placeholder="등록일"
:value="selectedRow?.regDt"
readonly
>
</div>
</div>
</div>
</div>
</div>
</template>
<template #action>
<VButton type="submit" color="info" raised>반려</VButton>
<VButton type="submit" color="primary" raised @click="updateIntegratedPaymentList">승인</VButton>
</template>
</VModal>
<!-- <VFlexTableWrapper :columns="params.flexWrapperColumn" :data="params.paymentParams">-->
<!--&lt;!&ndash;-->
<!-- Here we retrieve the internal wrapperState.-->
<!-- Note that we can not destructure it-->
<!-- &ndash;&gt;-->
<!-- <template #default="wrapperState">-->
<!-- <pre>data: {{ data }}</pre>-->
<!-- &lt;!&ndash; We can place any content inside the default slot&ndash;&gt;-->
<!-- <VFlexTableToolbar>-->
<!-- <template #left>-->
<!-- &lt;!&ndash; We can bind wrapperState.searchInput to any input &ndash;&gt;-->
<!-- <VField>-->
<!-- <VControl icon="lucide:search">-->
<!-- <input-->
<!-- v-model="wrapperState.searchInput"-->
<!-- type="text"-->
<!-- class="input is-rounded"-->
<!-- placeholder="Filter..."-->
<!-- >-->
<!-- </VControl>-->
<!-- </VField>-->
<!-- </template>-->
<!-- <template #right>-->
<!-- &lt;!&ndash; We can also bind wrapperState.limit &ndash;&gt;-->
<!-- <VField>-->
<!-- <VControl>-->
<!-- <div class="select is-rounded">-->
<!-- <select v-model="wrapperState.limit">-->
<!-- <option :value="1">-->
<!-- 1 results per page-->
<!-- </option>-->
<!-- <option :value="10">-->
<!-- 10 results per page-->
<!-- </option>-->
<!-- <option :value="15">-->
<!-- 15 results per page-->
<!-- </option>-->
<!-- <option :value="25">-->
<!-- 25 results per page-->
<!-- </option>-->
<!-- <option :value="50">-->
<!-- 50 results per page-->
<!-- </option>-->
<!-- </select>-->
<!-- </div>-->
<!-- </VControl>-->
<!-- </VField>-->
<!-- </template>-->
<!-- </VFlexTableToolbar>-->
<!-- &lt;!&ndash;-->
<!-- The VFlexTable "data" and "columns" props-->
<!-- will be inherited from parent VFlexTableWrapper-->
<!-- &ndash;&gt;-->
<!-- <VFlexTable rounded>-->
<!-- &lt;!&ndash; Custom "name" cell content &ndash;&gt;-->
<!-- <template #body-cell="{ row, column }">-->
<!-- <template v-if="column.key === 'name'">-->
<!-- <VAvatar-->
<!-- size="medium"-->
<!-- :picture="row.medias.avatar"-->
<!-- :badge="row.medias.badge"-->
<!-- :initials="row.initials"-->
<!-- />-->
<!-- <div>-->
<!-- <span class="dark-text" :title="row.name">-->
<!-- {{ row?.shortname }}-->
<!-- </span>-->
<!-- <VTextEllipsis-->
<!-- width="280px"-->
<!-- class="light-text"-->
<!-- :title="row.bio"-->
<!-- >-->
<!-- <small>{{ row?.bio }}</small>-->
<!-- </VTextEllipsis>-->
<!-- </div>-->
<!-- </template>-->
<!-- </template>-->
<!-- </VFlexTable>-->
<!-- &lt;!&ndash; Table Pagination with wrapperState.page binded&ndash;&gt;-->
<!-- <VFlexPagination-->
<!-- v-model:current-page="wrapperState.page"-->
<!-- class="mt-6"-->
<!-- :item-per-page="wrapperState.limit"-->
<!-- :total-items="wrapperState.total"-->
<!-- :max-links-displayed="5"-->
<!-- no-router-->
<!-- />-->
<!-- </template>-->
<!-- </VFlexTableWrapper>-->
</div>
</div>
<!-- <VButtons class="v-buttons-right-align">-->
<!-- <VButton-->
<!-- color="primary"-->
<!-- icon="fas fa-plus"-->
<!-- elevated-->
<!-- to="/app/PriceInsert"-->
<!-- >-->
<!-- 등록-->
<!-- </VButton>-->
<!-- </VButtons>-->
</div>
</template>
<style scoped lang="scss">
input[readonly] {
background-color: #f5f5f5; /* 살짝 회색 배경 */
color: #888; /* 글자색 연하게 */
cursor: not-allowed; /* 마우스 커서 변경 */
border-color: #e0e0e0; /* 테두리 연하게 */
}
.v-buttons-right-align {
display: flex;
justify-content: flex-end;
margin-top: 1rem; // 필요 시 간격 추가
}
.flex-table .flex-table-item {
background-color: var(--primary);
border: 0px;
}
.flex-table .flex-table-header {
padding: 0px;
}
.flex-table.is-table-clickable .flex-table-item:hover, .flex-table.is-table-clickable .flex-table-item:focus-within a:hover {
background-color: var(--primary)!important;
}
</style>

View File

@@ -0,0 +1,413 @@
<script setup lang="ts">
import {getDetailPrcs, updatePrcsNo} from '/src/service/priceApi'
import { type Person } from '/@src/utils/types'
const notyf = useNotyf()
const loading = ref(false)
const router = useRouter()
onBeforeMount(async ()=>{
const result = await getDetailPrcs(history.state.key)
getDetailList(result)
})
const showTable = ref(false)
const detailActionsOpen = ref(false)
const apprLine = defineModel<Person[]>()
const generalParams = reactive({
title: "",
content: "",
regSdat: "",
regEdat: "",
prvYn: true,
prvRsn : "",
prvPwd : "",
aiYn: true,
})
const params = reactive({
cateSelect: '',
prcsNo: '', // 키값
stCd:'',//결재상태 코드{ 등록중:0100[회수버튼],
// 회수: 0300, 반려: 0400, 등록 완료: 0200 }
prcsAttsColumn:[ //첨부파일 입력
{ key: 'logiFnm', label: '구분'},
{ key: 'data', label: '데이터'}
],
prcsAtts: [], //첨부파일 데이터
felxColumn: [
{ key: 'gubunNm', label: '구분'},
{ key: 'deptNm', label: '부서' },
{ key: 'sabun', label: '사번' },
{ key: 'name', label: '이름' },
{ key: 'attendNm', label: '비고' },
{ key: 'apprStat', label: '결재상태'},
{ key: 'date', label: '승인일자'},
],
priceData:[],
prcsBizsColumn: [ //견적사 입력
{ key: 'num', label: '구분', width: '10%' },
{ key: 'email', label: '이메일', editable: true, width: '50px' },
{ key: 'bizNo', label: '사업자번호', editable: true, width: '50px'},
],
prcsBizs: [], //견적사 입력 데이터
dtlSpecsColumn: [
{ key: 'num', label: '번호', editable: false },
{ key: 'itemNm', label: '품명', editable: true },
{ key: 'spec', label: '규격', editable: true },
{ key: 'unit', label: '단위', editable: true },
{ key: 'qty', label: '수량', editable: true },
{ key: '', label: '단가(VAT별도)', editable: false },
{ key: '', label: '금액(VAT별도)', editable: false },
],
dtlSpecs: [], //상세 규격 입력 데이터
})
function getDetailList(arg){
params.prcsNo = arg.prcsNo
params.stCd = arg.stCd
params.cateSelect = arg.cateNm
generalParams.title = arg.title
generalParams.content = arg.content
params.prcsBizs = arg.prcsBizs.map(req => ({
bizNo: req.bizNo,
email: req.email,
}))
params.dtlSpecs = arg.dtlSpecs
generalParams.regSdat = formatDate(arg.regSdat)
generalParams.regEdat = formatDate(arg.regEdat)
apprLine.value = arg.apprMst.apprReqs.map(req => ({
gubunNm: req.gubunNm,
deptCd: req.deptCd,
deptNm: req.deptNm,
sabun: req.sabun,
name: req.name,
apprNo: req.apprNo,
apprOrd: req.apprOrd,
apprStat: req.apprStat,
attendNm: req.attendNm
})) //비고 데이터 없음, 승인일자 없음 todo
}
const updateState = async () => {
let res = null
try {
loading.value = true
const paramsPrice = {
prcsNo : params.prcsNo
}
res = await updatePrcsNo(paramsPrice.prcsNo)
notyf.dismissAll()
if (res.request.status === 200) {
notyf.primary('회수 되었습니다.')
router.push({path: '/app/priceManagement'})
}
} catch (e) {
notyf.error(e.message)
} finally {
loading.value = false
}
}
function formatDate(dateStr) {
if (!dateStr) return ''
const date = new Date(dateStr)
return date.toLocaleDateString('ko-KR', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
}).replace(/\./g, '-').replace(/\s/g, '').replace(/-$/,'')
}
</script>
<template>
<div class="page-content is-navbar-lg">
<div class="datatable-wrapper">
<div class="table-container">
<table class="table datatable-table is-fullwidth">
<colgroup>
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
</colgroup>
<tbody>
<tr>
<td>분야</td>
<td colspan="2">
<span class="column is-7">
<input
:readonly=true
v-model="params.cateSelect"
class="input custom-text-filter"
placeholder="제목"
>
</span>
</td>
<td>제목</td>
<td colspan="3">
<div class="column is-fullhd">
<VField class="pr-2">
<VControl>
<input
:readonly=true
v-model="generalParams.title"
class="input custom-text-filter"
placeholder="제목"
>
</VControl>
</VField>
</div>
</td>
</tr>
<tr>
<td>내용</td>
<td colspan="6">
<div class="column is-fullhd">
<VField class="pr-2">
<VControl>
<textarea
:readonly=true
v-model="generalParams.content"
class="input custom-text-filter"
placeholder="내용"
/>
</VControl>
</VField>
</div>
</td>
</tr>
<tr>
<td>규격입력</td>
<td colspan="2">
<VButton
:color="params.btnChangeFlag? 'success':'primary'"
icon="fas fa-plus"
elevated
@click="detailActionsOpen = true"
>
<span> 상세 규격 입력</span>
</VButton>
<VModal
:open="detailActionsOpen"
actions="center"
title="상세 규격 입력"
size="contract-big"
cancelLabel="닫기"
@close="detailActionsOpen = false"
>
<template #content>
<ComVFlexTable
:key="params.dtlSpecs.length"
:data="params.dtlSpecs"
:columns="params.dtlSpecsColumn"
:separators="true"
:clickable="true"
>
<template #body-cell="{ row, column, index, value }">
<!-- : 특정 컬럼이면 input, 아니면 그냥 출력 -->
<div>
<input
:readonly=true
v-if="column.editable"
v-model="row[column.key]"
class="editable-input"
/>
<span v-else-if="column.key=='num'">{{index+1}}</span>
<span v-else>{{ value }}</span>
</div>
</template>
</ComVFlexTable>
</template>
<template #cancel>
</template>
</VModal>
</td>
<td>등록기간</td>
<td colspan="5">
<div class="columns">
<div class="column is-5">
<!-- <VDatePicker-->
<!-- v-model="generalParams.regSdat"-->
<!-- color="green"-->
<!-- disabled-->
<!-- trim-weeks-->
<!-- style="pointer-events: none;"-->
<!-- >-->
<!-- <template #default="{ inputValue, inputEvents }">-->
<!-- <VField>-->
<!-- <VControl icon="lucide:calendar">-->
<!-- <input-->
<!-- :readonly=true-->
<!-- class="input v-input"-->
<!-- type="text"-->
<!-- :value="inputValue"-->
<!-- v-on="inputEvents"-->
<!-- placeholder="시작일"-->
<!-- >-->
<!-- </VControl>-->
<!-- </VField>-->
<!-- </template>-->
<!-- </VDatePicker>-->
<input
:readonly=true
v-model="generalParams.regSdat"
class="input custom-text-filter"
placeholder="제목"
>
</div>
<div style="transform: translateY(15px)">~</div>
<div class="column is-5">
<!-- <VDatePicker-->
<!-- v-model="generalParams.regEdat"-->
<!-- color="green"-->
<!-- trim-weeks-->
<!-- disabledDates=""-->
<!-- >-->
<!-- <template #default="{ inputValue, inputEvents }">-->
<!-- <VField>-->
<!-- <VControl icon="lucide:calendar">-->
<!-- <input-->
<!-- :readonly=true-->
<!-- class="input v-input"-->
<!-- type="text"-->
<!-- :value="inputValue"-->
<!-- v-on="inputEvents"-->
<!-- placeholder="종료일"-->
<!-- >-->
<!-- </VControl>-->
<!-- </VField>-->
<!-- </template>-->
<!-- </VDatePicker>-->
<input
:readonly=true
v-model="generalParams.regEdat"
class="input custom-text-filter"
placeholder="제목"
>
</div>
</div>
</td>
</tr>
<tr>
<td>첨부파일</td>
<td colspan="3">
<VField class="file has-name is-right">
<div class="file-label">
<input
class="file-input"
type="file"
name="resume">
<span class="file-cta">
<span class="file-icon">
<i class="fas fa-cloud-upload-alt"/>
</span>
<span class="file-label">첨부파일</span>
</span>
<span class="file-name light-text">2022.xls</span>
</div>
</VField>
</td>
</tr>
<tr>
<td>견적요청</td>
<td colspan="6">
<VButton
color="primary"
icon="fas fa-plus"
elevated
@click="showTable = !showTable"
>
견적사 입력
</VButton>
<div v-if="showTable" class="mt-2">
<ComVFlexTable
:key="params.prcsBizs.length"
:data="params.prcsBizs"
:columns="params.prcsBizsColumn"
:compact="true"
:separators="true"
:clickable="true"
>
<template #body-cell="{ row, column, index, value }">
<div>
<!-- 다른 editable 컬럼은 input -->
<input
:readonly=true
v-if="column.editable"
v-model="row[column.key]"
class="editable-input"
/>
<span v-else-if="column.key=='num'">{{index + 1}}</span>
<!-- readonly 출력 -->
<span v-else>{{ value }}</span>
</div>
</template>
</ComVFlexTable>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<div class="column is-12">
<VField class="pr-2">
<VLabel class="has-fullwidth">
결재선
</VLabel>
</VField>
<ComVFlexTable
:data="apprLine"
:columns="params.felxColumn"
:separators="true"
:clickable="true"
:compact="true">
<template #body-cell="{ row, column, index, value }">
<!-- : 특정 컬럼이면 input, 아니면 그냥 출력 -->
<div>
<span>{{value}}</span>
</div>
</template>
</ComVFlexTable>
</div>
<div>
<VButton
v-if="params.stCd === '0100'"
color="warning"
@click.stop="updateState"
>
회수
</VButton>
<VButton
to="/app/priceManagement"
v-if="params.stCd === '0100' | '0200' | '0300' | '0400'"
color="info"
>
닫기
</VButton>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
.table tbody td {
color: var(--smoke-white);
}
.datatable-table {
padding: 12px 12px;
td:nth-child(1) {
background-color: var(--primary);
}
td:nth-child(3) {
background-color: var(--primary);
}
}
</style>

View File

@@ -0,0 +1,217 @@
<script setup lang="ts">
import { getPriceList } from '/@src/service/priceApi'
const router = useRouter()
onBeforeMount(async () => {
await getPriceListData()
const userSession = useUserSession()
params.sessionUser = userSession.user.data
})
const masks = ref({
modelValue: 'YYYY-MM-DD',
})
const searchParamsList = reactive({
cateCd : '', //분야코드
stCd : '', //등록상태
regNm : '', //담당자
regSdt: '',//등록시작일
regEdt: '',//등록종료일
page: 1,//페이지
row: 3 //아이템갯수
})
const params = reactive({
sessionUser:'',
priceData: [],
felxColumn: [
{ key: 'cateNm', label: '분야' },
{ key: 'title', label: '제목' },
{ key: 'regNm', label: '담당자' },
{ key: 'regDt', label: '등록일' },
{ key: 'stNm', label: '등록 상태' },
]
})
async function getPriceListData(){
const priceBase = {
params:{
regSdt: '1970-01-01',
regEdt: '2070-01-01',
page: '1',
row: '20'
}
}
const result = await getPriceList(priceBase)
params.priceData = result.content
}
const searchPrice = async () => {
const searchParams = {
params: {
cateCd : searchParamsList.cateCd, //분야코드
stCd : searchParamsList.stCd, //등록상태
regNm : searchParamsList.regNm, //담당자
regSdt: searchParamsList.regSdt,//등록시작일
regEdt: searchParamsList.regEdt,//등록종료일
page: 1,//페이지
row: 3 //아이템갯수
}
}
const result = await getPriceList(searchParams)
params.priceData = result.content
}
function getPriceDetail(){
//stCd 결재상태 코드 [등록전:0000, 등록중:0100, ]
if(params.sessionUser.sabun == arguments[0].regSabun && arguments[0].stCd == '0000'){
router.push({ path: '/app/priceUpdate', state: { key: arguments[0].prcsNo }})
}else{
router.push({ path: '/app/priceDetail', state: { key: arguments[0].prcsNo }})
}
}
</script>
<template>
<div class="page-content is-navbar-lg">
<div>
<div class="datatable-toolbar">
<div class="column is-2">
<VField class="pr-2">
<VLabel class="has-fullwidth">
분야
</VLabel>
<VCodeSelect
cd_grp=5
v-model="searchParamsList.cateCd"
placeholder="전체"/>
</VField>
</div>
<div class="column is-2">
<VField class="pr-2">
<VLabel class="has-fullwidth">
등록상태
</VLabel>
<VCodeSelect
cd_grp=1
v-model="searchParamsList.stCd"
placeholder="전체"/>
</VField>
</div>
<div class="column is-2">
<VField class="pr-2">
<VLabel class="has-fullwidth">
담당자
</VLabel>
<VControl>
<input
v-model="searchParamsList.regNm"
class="input custom-text-filter"
placeholder="담당자"
>
</VControl>
</VField>
</div>
<div class="column is-5">
<VField class="pr-2">
<VLabel class="has-fullwidth">
등록기간
</VLabel>
<VControl>
<div class="columns">
<div class="column is-6">
<VDatePicker
v-model.string="searchParamsList.regSdt"
color="green"
:masks="masks"
trim-weeks
>
<template #default="{ inputValue, inputEvents }">
<VField>
<VControl icon="lucide:calendar">
<input
class="input v-input"
type="text"
:value="inputValue"
v-on="inputEvents"
placeholder="시작일"
>
</VControl>
</VField>
</template>
</VDatePicker>
</div>
<div style="transform: translateY(15px)">~</div>
<div class="column is-6">
<VDatePicker
v-model.string="searchParamsList.regEdt"
color="green"
:masks="masks"
trim-weeks
>
<template #default="{ inputValue, inputEvents }">
<VField>
<VControl icon="lucide:calendar">
<input
class="input v-input"
type="text"
:value="inputValue"
v-on="inputEvents"
placeholder="종료일"
>
</VControl>
</VField>
</template>
</VDatePicker>
</div>
</div>
</VControl>
</VField>
</div>
<div class="column is-1">
<div style="padding-top:20px;float:right;">
<VButtons>
<VButton
color="primary"
elevated
icon="fas fa-search"
@click.stop="searchPrice"
>
검색
</VButton>
</VButtons>
</div>
</div>
</div>
<div class="datatable-wrapper">
<ComVFlexTable
:data="params.priceData"
:columns="params.felxColumn"
:separators="true"
:clickable="true"
:compact="true"
@rowClick="getPriceDetail"
/>
</div>
</div>
<VButtons class="v-buttons-right-align">
<VButton
color="primary"
icon="fas fa-plus"
elevated
to="/app/PriceInsert"
>
등록
</VButton>
</VButtons>
</div>
</template>
<style scoped lang="scss">
.v-buttons-right-align {
display: flex;
justify-content: flex-end;
margin-top: 1rem; // 필요 시 간격 추가
}
</style>

View File

@@ -0,0 +1,509 @@
<script setup lang="ts">
import {getDetailPrcs, updatePrice} from '/src/service/priceApi'
import { type Person } from '/@src/utils/types'
const notyf = useNotyf()
const loading = ref(false)
const router = useRouter()
onBeforeMount(async ()=>{
const result = await getDetailPrcs(history.state.key)
getDetailList(result)
})
const showTable = ref(false)
const detailActionsOpen = ref(false)
const apprLine = defineModel<Person[]>()
const generalParams = reactive({
title: "",
content: "",
regSdat: "",
regEdat: "",
prvYn: true,
prvRsn : "",
prvPwd : "",
aiYn: true,
})
const params = reactive({
cateSelect: '',
prcsNo: '',
stCd: '', //상태코드 등록 전 : 0000
prcsAttsColumn:[ //첨부파일 입력
{ key: 'logiFnm', label: '구분'},
{ key: 'data', label: '데이터'}
],
prcsAtts: [], //첨부파일 데이터
felxColumn: [
{ key: 'gubunCd', label: '구분'},
{ key: 'deptNm', label: '부서' },
{ key: 'sabun', label: '사번' },
{ key: 'name', label: '이름' },
{ key: 'attendCd', label: '비고' },
{ key: 'apprStat', label: '결재상태'},
{ key: 'date', label: '승인일자'},
],
priceData:[],
prcsBizsColumn: [ //견적사 입력
{ key: 'num', label: '구분', width: '10%' },
{ key: 'email', label: '이메일', editable: true, width: '50px' },
{ key: 'bizNo', label: '사업자번호', editable: true, width: '50px'},
{ key: 'actions', label: '동작', width: '100px'}
],
prcsBizs: [], //견적사 입력 데이터
dtlSpecsColumn: [
{ key: 'num', label: '번호', editable: false },
{ key: 'itemNm', label: '품명', editable: true },
{ key: 'spec', label: '규격', editable: true },
{ key: 'unit', label: '단위', editable: true },
{ key: 'qty', label: '수량', editable: true },
{ key: '', label: '단가(VAT별도)', editable: false },
{ key: '', label: '금액(VAT별도)', editable: false },
{ key: '', label: '삭제', editable: false },
],
dtlSpecs: [], //상세 규격 입력 데이터
detailData :[
{ cateNm: '홍길동', age: 30, email: 'hong@example.com' },
{ name: '김철수', age: 28, email: 'kim@example.com' },
],
btnChangeFlag: false
})
function getDetailList(arg){
params.prcsNo = arg.prcsNo
params.stCd = arg.stCd
params.cateSelect = arg.cateCd
generalParams.title = arg.title
generalParams.content = arg.content
params.prcsBizs = arg.prcsBizs.map(req => ({
bizNo: req.bizNo,
email: req.email,
}))
params.dtlSpecs = arg.dtlSpecs
generalParams.regSdat = arg.regSdat
generalParams.regEdat = arg.regEdat
apprLine.value = arg.apprMst.apprReqs.map(req => ({
gubunCd: req.gubunCd,
deptCd: req.deptCd,
deptNm: req.deptNm,
sabun: req.sabun,
name: req.name,
apprNo: req.apprNo,
apprOrd: req.apprOrd,
apprStat: req.apprStat,
attendCd: req.attendCd
})) //비고 데이터 없음, 승인일자 없음 todo
}
const changeButton = () => {
//todo 상세 규격 api 테우기
params.btnChangeFlag = true
close()
}
const validation = () => {
notyf.dismissAll() //todo
if(generalParams.regSdat > generalParams.regEdat){
notyf.error("등록 종료일은 등록 시작일보다 빠를 수 없습니다.")
return
}
if(apprLine.value.length < 2){
notyf.error("결재선은 두 명이상 입력해주세요.")
return
}
}
const updatePriceOne = async () => {
let res = null
try{
loading.value = true
validation()
const paramsPrice ={
prcsNo : params.prcsNo,
cateCd : params.cateSelect,
title: generalParams.title,
content: generalParams.content,
regSdat: formatDate(generalParams.regSdat),
regEdat: formatDate(generalParams.regEdat),
prvYn: false,
prvRsn : "",
prvPwd : "",
aiYn: false,
prcsBizs: params.prcsBizs.map(({ num, ...rest }) => rest), //견적사 입력 데이터
dtlSpecs: [], //todo
// params.dtlSpecs.map(({ num, ...rest }) => rest), //상세 규격 입력 데이터
prcsAtts: params.prcsAtts, //첨부파일 데이터
apprReqs: apprLine.value.map(({ deptNm, ...rest }) => rest), //결재선 데이터
}
res = await updatePrice(paramsPrice)
notyf.dismissAll()
if(res.request.status == '200'){
notyf.primary('수정 되었습니다.')
router.push({path: '/app/priceManagement'})
}
}catch(e){
notyf.error(e.message)
}finally {
loading.value = false
}
}
const addNewEstimateRow = () => {
const newRow = {}
params.prcsBizsColumn.forEach(col => {
if (col.key && col.key !== 'actions') {
newRow[col.key] = ''
}
})
params.prcsBizs.push(newRow)
}
const onDelete = (index: number) => {
if(params.prcsBizs.length-1 !== params.prcsBizsColumn.length
|| (params.prcsBizsColumn.length == 4 && params.prcsBizs.length-1 == params.prcsBizsColumn.length))
{
params.prcsBizs.splice(index, 1)
}
}
const addNewDetailRow = () => {
const newRow = {}
params.dtlSpecsColumn.forEach(col => {
if (col.key) {
newRow[col.key] = ''
}
})
params.dtlSpecs.push(newRow)
console.log(params.dtlSpecs.length)
}
const onDetailDelete = (index: number) => {
if(params.dtlSpecs.length-1 !== params.dtlSpecsColumn.length
|| (params.dtlSpecsColumn.length == 8 && params.dtlSpecs.length-1 == params.dtlSpecsColumn.length))
{
params.dtlSpecs.splice(index, 1)
}
}
function formatDate(dateStr) {
if (!dateStr) return ''
const date = new Date(dateStr)
return date.toLocaleDateString('ko-KR', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
}).replace(/\./g, '-').replace(/\s/g, '').replace(/-$/,'')
}
</script>
<template>
<div class="page-content is-navbar-lg">
<div class="datatable-wrapper">
<div class="table-container">
<table class="table datatable-table is-fullwidth">
<colgroup>
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
<col style="width: 10%;">
</colgroup>
<tbody>
<tr>
<td>분야</td>
<td colspan="2">
<span class="column is-7">
<VField class="pr-2">
<VCodeSelect
cd_grp=5
v-model="params.cateSelect"/>
</VField>
</span>
</td>
<td>제목</td>
<td colspan="3">
<div class="column is-fullhd">
<VField class="pr-2">
<VControl>
<input
v-model="generalParams.title"
class="input custom-text-filter"
placeholder="제목"
>
</VControl>
</VField>
</div>
</td>
</tr>
<tr>
<td>내용</td>
<td colspan="6">
<div class="column is-fullhd">
<VField class="pr-2">
<VControl>
<textarea
v-model="generalParams.content"
class="input custom-text-filter"
placeholder="내용"
/>
</VControl>
</VField>
</div>
</td>
</tr>
<tr>
<td>규격입력</td>
<td colspan="2">
<VButton
:color="params.btnChangeFlag? 'success':'primary'"
icon="fas fa-plus"
elevated
@click="detailActionsOpen = true"
>
<span v-if="params.btnChangeFlag == false"> 상세 규격 입력</span>
<span v-else-if="params.btnChangeFlag"> 상세 규격 등록 완료</span>
</VButton>
<VModal
:open="detailActionsOpen"
actions="center"
title="상세 규격 입력"
size="contract-big"
@close="detailActionsOpen = false"
>
<template #content>
<VButton color="success">일괄업로드</VButton>
<ComVFlexTable
:key="params.dtlSpecs.length"
:data="params.dtlSpecs"
:columns="params.dtlSpecsColumn"
:separators="true"
:clickable="true"
>
<template #body-cell="{ row, column, index, value }">
<!-- : 특정 컬럼이면 input, 아니면 그냥 출력 -->
<div>
<input
v-if="column.editable"
v-model="row[column.key]"
class="editable-input"
/>
<span v-else-if="column.key=='num'">{{index+1}}</span>
<span v-else class="lnil lnil-close"
@click="onDetailDelete(index)">{{ value }}</span>
</div>
</template>
</ComVFlexTable>
<div class="mt-2">
<VButton
color="primary"
icon="fas fa-plus"
@click="addNewDetailRow"
>
추가
</VButton>
</div>
</template>
<template #action>
<VButton color="primary" @click="changeButton">
등록
</VButton>
</template>
</VModal>
</td>
<td>등록기간</td>
<td colspan="5">
<div class="columns">
<div class="column is-5">
<VDatePicker
v-model="generalParams.regSdat"
color="green"
trim-weeks
>
<template #default="{ inputValue, inputEvents }">
<VField>
<VControl icon="lucide:calendar">
<input
class="input v-input"
type="text"
:value="inputValue"
v-on="inputEvents"
placeholder="시작일"
>
</VControl>
</VField>
</template>
</VDatePicker>
</div>
<div style="transform: translateY(15px)">~</div>
<div class="column is-5">
<VDatePicker
v-model="generalParams.regEdat"
color="green"
trim-weeks
disabledDates=""
>
<template #default="{ inputValue, inputEvents }">
<VField>
<VControl icon="lucide:calendar">
<input
class="input v-input"
type="text"
:value="inputValue"
v-on="inputEvents"
placeholder="종료일"
>
</VControl>
</VField>
</template>
</VDatePicker>
</div>
</div>
</td>
</tr>
<tr>
<td>첨부파일</td>
<td colspan="3">
<VField class="file has-name is-right">
<div class="file-label">
<input
class="file-input"
type="file"
name="resume">
<span class="file-cta">
<span class="file-icon">
<i class="fas fa-cloud-upload-alt"/>
</span>
<span class="file-label">첨부파일</span>
</span>
<span class="file-name light-text">2022.xls</span>
</div>
</VField>
</td>
</tr>
<tr>
<td>견적요청</td>
<td colspan="6">
<VButton
color="primary"
icon="fas fa-plus"
elevated
@click="showTable = !showTable"
>
견적사 입력
</VButton>
<div v-if="showTable" class="mt-2">
<ComVFlexTable
:key="params.prcsBizs.length"
:data="params.prcsBizs"
:columns="params.prcsBizsColumn"
:compact="true"
:separators="true"
:clickable="true"
>
<template #body-cell="{ row, column, index, value }">
<div>
<!-- 다른 editable 컬럼은 input -->
<input
v-if="column.editable"
v-model="row[column.key]"
class="editable-input"
/>
<span v-else-if="column.key=='num'">{{index + 1}}</span>
<!-- readonly 출력 -->
<span v-else class="lnil lnil-close"
@click="onDelete(index)">{{ value }}</span>
</div>
</template>
</ComVFlexTable>
<div class="mt-2">
<VButton
color="primary"
icon="fas fa-plus"
@click="addNewEstimateRow"
>
추가
</VButton>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<div class="column is-12">
<VUser v-model="apprLine"/>
</div>
<div class="column is-12">
<VField class="pr-2">
<VLabel class="has-fullwidth">
결재선
</VLabel>
</VField>
<ComVFlexTable
:data="apprLine"
:columns="params.felxColumn"
:separators="true"
:clickable="true"
:compact="true">
<template #body-cell="{ row, column, index, value }">
<!-- : 특정 컬럼이면 input, 아니면 그냥 출력 -->
<div>
<span v-if="column.key=='gubunCd'" class="column">
<VField class="pr-1">
<VCodeSelect
cd_grp=7
v-model="row.gubunCd"/>
</VField>
</span>
<span v-else-if="column.key=='attendCd'" class="column">
<VField class="pr-1">
<VCodeSelect
cd_grp=6
v-model="row.attendCd"/>
</VField>
</span>
<span v-else
@click="onDetailDelete(index)">{{value}}</span>
</div>
</template>
</ComVFlexTable>
</div>
<VButton
color="primary"
@click.stop="updatePriceOne"
raised>
수정
</VButton>
<VButton
to="/app/priceManagement"
v-if="params.stCd === '0000'"
color="info"
>
닫기
</VButton>
</div>
</div>
</template>
<style scoped lang="scss">
.table tbody td {
color: var(--smoke-white);
}
.datatable-table {
padding: 12px 12px;
td:nth-child(1) {
background-color: var(--primary);
}
td:nth-child(3) {
background-color: var(--primary);
}
}
</style>

5
src/pages/auth.vue Normal file
View File

@@ -0,0 +1,5 @@
<template>
<AuthLayout>
<RouterView />
</AuthLayout>
</template>

0
src/pages/auth/index.vue Normal file
View File

149
src/pages/auth/login-2.vue Normal file
View File

@@ -0,0 +1,149 @@
<script setup lang="ts">
const isLoading = ref(false)
const router = useRouter()
const route = useRoute()
const notyf = useNotyf()
const token = useUserToken()
const redirect = route.query.redirect as string
const handleLogin = async () => {
if (!isLoading.value) {
isLoading.value = true
await sleep(2000)
token.value = 'logged-in'
notyf.dismissAll()
notyf.primary('Welcome back, Erik Kovalsky')
if (redirect) {
router.push(redirect)
}
else {
router.push('/sidebar/dashboards')
}
isLoading.value = false
}
}
useHead({
title: 'Auth Login 2 - Vuero',
})
</script>
<template>
<div class="auth-wrapper-inner columns is-gapless">
<!-- Image section (hidden on mobile) -->
<div class="column login-column is-8 h-hidden-mobile h-hidden-tablet-p hero-banner">
<div class="hero login-hero is-fullheight is-app-grey">
<div class="hero-body is-justify-content-center is-fullwidth">
<div class="is-flex is-justify-content-center is-fullwidth">
<img
class="light-image has-light-shadow has-light-border"
src="/images/illustrations/apps/vuero-banking-light.webp"
alt=""
>
<img
class="dark-image has-light-shadow"
src="/images/illustrations/apps/vuero-banking-dark.webp"
alt=""
>
</div>
</div>
</div>
</div>
<!-- Form section -->
<div class="column is-4">
<div class="hero is-fullheight is-white">
<div class="hero-heading">
<div class="auth-logo">
<RouterLink to="/">
<AnimatedLogo
width="36px"
height="36px"
/>
</RouterLink>
<VDarkmodeToggle />
</div>
</div>
<div class="hero-body">
<div class="container">
<div class="columns">
<div class="column is-12">
<div class="auth-content">
<h2>Welcome Back.</h2>
<p>Please sign in to your account</p>
<RouterLink to="/auth/signup-2">
I do not have an account yet
</RouterLink>
</div>
<div class="auth-form-wrapper">
<!-- Login Form -->
<form
method="post"
novalidate
@submit.prevent="handleLogin"
>
<div class="login-form">
<!-- Username -->
<VField>
<VControl icon="lucide:user">
<VInput
type="text"
placeholder="Username"
autocomplete="username"
/>
</VControl>
</VField>
<!-- Password -->
<VField>
<VControl icon="lucide:lock">
<VInput
type="password"
placeholder="Password"
autocomplete="current-password"
/>
</VControl>
</VField>
<!-- Switch -->
<VField>
<VControl class="setting-item">
<VCheckbox
label="Remember me"
paddingless
/>
</VControl>
</VField>
<!-- Submit -->
<div class="login">
<VButton
:loading="isLoading"
color="primary"
type="submit"
bold
fullwidth
raised
>
Sign In
</VButton>
</div>
<div class="forgot-link has-text-centered">
<a>Forgot Password?</a>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>

130
src/pages/auth/login-3.vue Normal file
View File

@@ -0,0 +1,130 @@
<script setup lang="ts">
const isLoading = ref(false)
const router = useRouter()
const route = useRoute()
const notyf = useNotyf()
const token = useUserToken()
const redirect = route.query.redirect as string
const handleLogin = async () => {
if (!isLoading.value) {
isLoading.value = true
await sleep(2000)
token.value = 'logged-in'
notyf.dismissAll()
notyf.primary('Welcome back, Erik Kovalsky')
if (redirect) {
router.push(redirect)
}
else {
router.push('/sidebar/dashboards')
}
isLoading.value = false
}
}
useHead({
title: 'Auth Login 3 - Vuero',
})
</script>
<template>
<div class="auth-wrapper-inner is-single">
<LandingGrids class="is-contrasted" />
<!--Fake navigation-->
<div class="auth-nav">
<div class="left" />
<div class="center">
<RouterLink
to="/"
class="header-item"
>
<AnimatedLogo
width="38px"
height="38px"
/>
</RouterLink>
</div>
<div class="right">
<VDarkmodeToggle />
</div>
</div>
<!--Single Centered Form-->
<div class="single-form-wrap is-relative">
<div class="inner-wrap">
<!--Form Title-->
<div class="auth-head">
<h2>Welcome Back.</h2>
<p>Please sign in to your account</p>
<RouterLink to="/auth/signup-3">
I do not have an account yet
</RouterLink>
</div>
<!--Form-->
<div class="form-card">
<form
method="post"
novalidate
@submit.prevent="handleLogin"
>
<div class="login-form">
<VField>
<VControl icon="lucide:user">
<VInput
type="text"
placeholder="Username"
autocomplete="username"
/>
</VControl>
</VField>
<VField>
<VControl icon="lucide:lock">
<VInput
type="password"
placeholder="Password"
autocomplete="current-password"
/>
</VControl>
</VField>
<!-- Switch -->
<VField>
<VControl class="setting-item">
<VCheckbox
label="Remember me"
color="primary"
paddingless
/>
</VControl>
</VField>
<!-- Submit -->
<div class="login">
<VButton
:loading="isLoading"
type="submit"
color="primary"
bold
fullwidth
raised
>
Sign In
</VButton>
</div>
</div>
</form>
</div>
<div class="forgot-link has-text-centered">
<a>Forgot Password?</a>
</div>
</div>
</div>
</div>
</template>

759
src/pages/auth/login.vue Normal file
View File

@@ -0,0 +1,759 @@
<script setup lang="ts">
type StepId = 'login' | 'forgot-password'
const step = ref<StepId>('login')
const isLoading = ref(false)
const router = useRouter()
const route = useRoute()
const notyf = useNotyf()
const token = useUserToken()
const redirect = route.query.redirect as string
const handleLogin = async () => {
if (!isLoading.value) {
isLoading.value = true
await sleep(2000)
console.log('set token logged-in')
token.value = 'logged-in'
notyf.dismissAll()
notyf.primary('Welcome back, Erik Kovalsky')
if (redirect) {
router.push(redirect)
}
else {
router.push('/sidebar/dashboards')
}
isLoading.value = false
}
}
useHead({
title: 'Auth Login 1 - Vuero',
})
</script>
<template>
<div class="modern-login">
<div class="underlay h-hidden-mobile h-hidden-tablet-p" />
<div class="columns is-gapless is-vcentered">
<div class="column is-relative is-8 h-hidden-mobile h-hidden-tablet-p">
<div class="hero is-fullheight is-image">
<div class="hero-body">
<div class="container">
<div class="columns">
<div class="column">
<img
class="hero-image"
src="/images/illustrations/login/station.svg"
alt=""
>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="column is-4 is-relative">
<div class="top-tools">
<RouterLink
to="/"
class="top-logo"
>
<AnimatedLogo
width="38px"
height="38px"
/>
</RouterLink>
<VDarkmodeToggle />
</div>
<div class="is-form">
<div class="is-form-inner">
<div
class="form-text"
:class="[step !== 'login' && 'is-hidden']"
>
<h2>Sign In</h2>
<p>Welcome back to your account.</p>
</div>
<div
class="form-text"
:class="[step === 'login' && 'is-hidden']"
>
<h2>Recover Account</h2>
<p>Reset your account password.</p>
</div>
<form
method="post"
novalidate
:class="[step !== 'login' && 'is-hidden']"
class="login-wrapper"
@submit.prevent="handleLogin"
>
<VMessage color="primary">
<div>
<strong class="pr-1">email:</strong>
<span>john.doe@cssninja.io</span>
</div>
<div>
<strong class="pr-1">password:</strong>
<span>ada.lovelace</span>
</div>
</VMessage>
<VField>
<VControl icon="lnil lnil-envelope autv-icon">
<VLabel class="auth-label">
Email Address
</VLabel>
<VInput
type="email"
autocomplete="current-password"
/>
</VControl>
</VField>
<VField>
<VControl icon="lnil lnil-lock-alt autv-icon">
<VLabel class="auth-label">
Password
</VLabel>
<VInput
type="password"
autocomplete="current-password"
/>
</VControl>
</VField>
<VField>
<VControl class="is-flex">
<VLabel
raw
class="remember-toggle"
>
<VInput
raw
type="checkbox"
/>
<span class="toggler">
<span class="active">
<VIcon
icon="lucide:check"
/>
</span>
<span class="inactive">
<VIcon
icon="lucide:circle"
/>
</span>
</span>
</VLabel>
<VLabel
raw
class="remember-me"
>
Remember Me
</VLabel>
<a
tabindex="0"
role="button"
@keydown.enter.prevent="step = 'forgot-password'"
@click="step = 'forgot-password'"
>
Forgot Password?
</a>
</VControl>
</VField>
<div class="button-wrap has-help">
<VButton
id="login-button"
:loading="isLoading"
color="primary"
type="submit"
size="big"
rounded
raised
bold
>
Confirm
</VButton>
<span>
Or
<RouterLink to="/auth/signup-1">Create</RouterLink>
an account.
</span>
</div>
</form>
<form
method="post"
novalidate
:class="[step !== 'forgot-password' && 'is-hidden']"
class="login-wrapper"
@submit.prevent
>
<p class="recover-text">
Enter your email and click on the confirm button to reset your password.
We'll send you an email detailing the steps to complete the procedure.
</p>
<VField>
<VControl icon="lnil lnil-envelope autv-icon">
<VLabel class="auth-label">
Email Address
</VLabel>
<VInput
type="email"
autocomplete="current-password"
/>
</VControl>
</VField>
<div class="button-wrap">
<VButton
color="white"
size="big"
lower
rounded
@click="step = 'login'"
>
Cancel
</VButton>
<VButton
color="primary"
size="big"
type="submit"
lower
rounded
solid
@click="step = 'login'"
>
Confirm
</VButton>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.modern-login {
position: relative;
background: var(--white);
min-height: 100vh;
.column {
&.is-relative {
position: relative;
}
}
.hero {
&.has-background-image {
position: relative;
.hero-overlay {
position: absolute;
top: 0;
inset-inline-start: 0;
width: 100%;
height: 100%;
background: #5d4298 !important;
opacity: 0.6;
}
}
}
.underlay {
display: block;
position: absolute;
top: 0;
inset-inline-start: 0;
width: 66.6%;
height: 100%;
background: #fdfdfd;
z-index: 0;
}
.top-tools {
display: flex;
align-items: center;
justify-content: space-between;
max-width: 400px;
margin: 0 auto;
padding: 0 1.25rem;
margin-bottom: 5rem;
.dark-mode {
transform: scale(0.6);
z-index: 2;
}
.top-logo {
display: flex;
justify-content: center;
align-items: center;
z-index: 1;
img {
display: block;
width: 100%;
max-width: 50px;
margin: 0 auto;
}
.iconify {
height: 50px;
width: 50px;
}
}
}
.is-image {
position: relative;
border-inline-end: 1px solid var(--fade-grey);
.hero-image {
position: relative;
z-index: 2;
display: block;
margin: -80px auto 0;
max-width: 60%;
width: 60%;
}
}
.is-form {
position: relative;
max-width: 400px;
margin: 0 auto;
form {
animation: fadeInLeft 0.5s;
}
.form-text {
padding: 0 20px;
animation: fadeInLeft 0.5s;
h2 {
font-family: var(--font-alt);
font-weight: 400;
font-size: 2rem;
color: var(--primary);
}
p {
color: var(--muted-grey);
margin-top: 10px;
}
}
.recover-text {
font-size: 0.9rem;
color: var(--dark-text);
}
.login-wrapper {
padding: 30px 20px;
.control {
position: relative;
width: 100%;
margin-top: 16px;
.input {
padding-top: 14px;
height: 60px;
border-radius: 10px;
padding-inline-start: 55px;
transition: all 0.3s; // transition-all test
&:focus {
background: color-mix(in oklab, var(--fade-grey), white 6%);
border-color: var(--placeholder);
~ .auth-label,
~ .autv-icon .iconify {
color: var(--muted-grey);
}
}
}
.error-text {
color: var(--danger);
font-size: 0.8rem;
display: none;
padding: 2px 6px;
}
.auth-label {
position: absolute;
top: 6px;
inset-inline-start: 55px;
font-size: 0.8rem;
color: var(--dark-text);
font-weight: 500;
z-index: 2;
transition: all 0.3s; // transition-all test
}
.autv-icon,
:deep(.autv-icon) {
position: absolute;
top: 0;
inset-inline-start: 0;
height: 60px;
width: 60px;
display: flex;
justify-content: center;
align-items: center;
font-size: 24px;
color: var(--placeholder);
transition: all 0.3s;
}
&.has-validation {
.validation-icon {
position: absolute;
top: 0;
inset-inline-end: 0;
height: 60px;
width: 60px;
display: none;
justify-content: center;
align-items: center;
.icon-wrapper {
height: 20px;
width: 20px;
display: flex;
justify-content: center;
align-items: center;
border-radius: var(--radius-rounded);
.iconify {
height: 10px;
width: 10px;
stroke-width: 3px;
color: var(--white);
}
}
&.is-success {
.icon-wrapper {
background: var(--success);
}
}
&.is-error {
.icon-wrapper {
background: var(--danger);
}
}
}
&.has-success {
.validation-icon {
&.is-success {
display: flex;
}
&.is-error {
display: none;
}
}
}
&.has-error {
.input {
border-color: var(--danger);
}
.error-text {
display: block;
}
.validation-icon {
&.is-error {
display: flex;
}
&.is-success {
display: none;
}
}
}
}
&.is-flex {
display: flex;
align-items: center;
a {
display: block;
margin-inline-start: auto;
color: var(--muted-grey);
font-weight: 500;
font-size: 0.9rem;
transition: color 0.3s;
&:hover,
&:focus {
color: var(--primary);
}
}
.remember-me {
font-size: 0.9rem;
color: var(--muted-grey);
font-weight: 500;
}
}
}
.button-wrap {
margin: 40px 0;
&.has-help {
display: flex;
align-items: center;
> span {
margin-inline-start: 12px;
font-family: var(--font);
a {
color: var(--primary);
font-weight: 500;
padding: 0 2px;
}
}
}
.button {
height: 46px;
width: 140px;
margin-inline-start: 6px;
&:first-child {
&:hover {
opacity: 0.8;
}
}
}
}
}
}
}
.remember-toggle {
width: 65px;
display: block;
position: relative;
cursor: pointer;
font-size: 22px;
user-select: none;
transform: scale(0.9);
input {
position: absolute;
opacity: 0;
cursor: pointer;
&:checked ~ .toggler {
border-color: var(--primary);
.active,
.inactive {
transform: translateX(calc(var(--transform-direction) * 100%)) rotate(360deg);
}
.active {
opacity: 1;
}
.inactive {
opacity: 0;
}
}
}
.toggler {
position: relative;
display: block;
height: 34px;
width: 61px;
border: 2px solid var(--placeholder);
border-radius: 100px;
transition: all 0.3s; // transition-all test
.active,
.inactive {
position: absolute;
top: 2px;
inset-inline-start: 2px;
height: 26px;
width: 26px;
border-radius: var(--radius-rounded);
background: black;
display: flex;
justify-content: center;
align-items: center;
transform: translateX(calc(var(--transform-direction) * 0))
rotate(calc(var(--transform-direction) * 0));
transition: all 0.3s ease;
.iconify {
color: var(--white);
font-size: 14px;
}
}
.inactive {
background: var(--placeholder);
border-color: var(--placeholder);
opacity: 1;
z-index: 1;
}
.active {
background: var(--primary);
border-color: var(--primary);
opacity: 0;
z-index: 0;
}
}
}
@media only screen and (width <= 767px) {
.modern-login {
.top-logo {
top: 30px;
}
.dark-mode {
top: 36px;
inset-inline-end: 44px;
}
.is-form {
padding-top: 100px;
}
}
}
@media only screen and (width >= 768px) and (width <= 1024px) and (orientation: portrait) {
.modern-login {
.top-logo {
.iconify {
height: 60px;
width: 60px;
}
}
.dark-mode {
top: -58px;
inset-inline-end: 30%;
}
.columns {
display: flex;
height: 100vh;
}
}
}
/* ==========================================================================
Dark mode
========================================================================== */
.is-dark {
.modern-login {
background: var(--dark-sidebar);
.underlay {
background: color-mix(in oklab, var(--dark-sidebar), white 10%);
}
.is-image {
border-color: color-mix(in oklab, var(--dark-sidebar), white 10%);
}
.is-form {
.form-text {
h2 {
color: var(--primary);
}
}
.login-wrapper {
.control {
&.is-flex {
a:hover {
color: var(--primary);
}
}
.input {
background: color-mix(in oklab, var(--dark-sidebar), white 4%);
&:focus {
border-color: var(--primary);
~ .autv-icon {
.iconify {
color: var(--primary);
}
}
}
}
.auth-label {
color: var(--light-text);
}
}
.button-wrap {
&.has-help {
span {
color: var(--light-text);
a {
color: var(--primary);
}
}
}
}
}
}
}
.remember-toggle {
input {
&:checked + .toggler {
border-color: var(--primary);
> span {
background: var(--primary);
}
}
}
.toggler {
border-color: color-mix(in oklab, var(--dark-sidebar), white 12%);
> span {
background: color-mix(in oklab, var(--dark-sidebar), white 12%);
}
}
}
}
</style>

1501
src/pages/auth/signup-1.vue Normal file

File diff suppressed because it is too large Load Diff

256
src/pages/auth/signup-2.vue Normal file
View File

@@ -0,0 +1,256 @@
<script setup lang="ts">
import { toTypedSchema } from '@vee-validate/zod'
import { useForm } from 'vee-validate'
import { z } from 'zod'
const router = useRouter()
const notyf = useNotyf()
const isLoading = ref(false)
const { t } = useI18n()
// This is the Zod schema for the form input
// It's used to define the shape that the form data will have
const zodSchema = z
.object({
name: z
.string({
required_error: t('auth.errors.name.required'),
})
.min(1, t('auth.errors.name.required')),
email: z
.string({
required_error: t('auth.errors.email.required'),
})
.email(t('auth.errors.email.format')),
password: z
.string({
required_error: t('auth.errors.password.required'),
})
.min(8, t('auth.errors.password.length')),
passwordCheck: z.string({
required_error: t('auth.errors.passwordCheck.required'),
}),
promotional: z.boolean(),
})
// Refine is used to add custom validation rules to the schema
.refine(data => data.password === data.passwordCheck, {
message: t('auth.errors.passwordCheck.match'),
path: ['passwordCheck'],
})
// Zod has a great infer method that will
// infer the shape of the schema into a TypeScript type
type FormInput = z.infer<typeof zodSchema>
// Define a validation schema
const validationSchema = toTypedSchema(zodSchema)
// Set initial values for the form
const initialValues = {
name: '',
email: '',
password: '',
passwordCheck: '',
promotional: false,
} satisfies FormInput
// here we create a vee-validate form context that
// will be used in all vuero form components
const { handleSubmit } = useForm({
validationSchema,
initialValues,
})
const onSignup = handleSubmit(async (values) => {
console.log('handleSignup values')
console.table(values)
if (!isLoading.value) {
isLoading.value = true
await sleep(800)
notyf.dismissAll()
notyf.primary('Welcome, Erik Kovalsky')
router.push('/sidebar/dashboards')
isLoading.value = false
}
})
useHead({
title: 'Auth Signup 2 - Vuero',
})
</script>
<template>
<div class="auth-wrapper-inner columns is-gapless">
<!-- Form section -->
<div class="column is-5">
<div class="hero is-fullheight is-white">
<div class="hero-heading">
<div class="auth-logo">
<LanguageDropdown />
<RouterLink to="/">
<AnimatedLogo
class="top-logo"
width="36px"
height="36px"
/>
</RouterLink>
<VDarkmodeToggle />
</div>
</div>
<div class="hero-body">
<div class="container">
<div class="columns">
<div class="column is-12">
<div class="auth-content">
<h2>{{ t('auth.title') }}</h2>
<p>{{ t('auth.subtitle') }}</p>
<RouterLink to="/auth/login-2">
{{ t('auth.action.login') }}
</RouterLink>
</div>
<div class="auth-form-wrapper">
<!-- Login Form -->
<form
method="post"
novalidate
@submit="onSignup"
>
<div class="login-form">
<!-- Input -->
<VField
id="name"
v-slot="{ field }"
>
<VControl icon="lucide:user">
<VInput
type="text"
:placeholder="t('auth.placeholder.name')"
autocomplete="name"
/>
<p
v-if="field?.errorMessage"
class="help is-danger"
>
{{ field.errorMessage }}
</p>
</VControl>
</VField>
<!-- Input -->
<VField
id="email"
v-slot="{ field }"
>
<VControl icon="lucide:mail">
<VInput
type="text"
:placeholder="t('auth.placeholder.email')"
autocomplete="email"
/>
<p
v-if="field?.errorMessage"
class="help is-danger"
>
{{ field.errorMessage }}
</p>
</VControl>
</VField>
<!-- Input -->
<VField
id="password"
v-slot="{ field }"
>
<VControl icon="lucide:lock">
<VInput
type="password"
:placeholder="t('auth.placeholder.password')"
autocomplete="new-password"
/>
<p
v-if="field?.errorMessage"
class="help is-danger"
>
{{ field.errorMessage }}
</p>
</VControl>
</VField>
<!-- Input -->
<VField
id="passwordCheck"
v-slot="{ field }"
>
<VControl icon="lucide:lock">
<VInput
type="password"
:placeholder="t('auth.placeholder.passwordCheck')"
/>
<p
v-if="field?.errorMessage"
class="help is-danger"
>
{{ field.errorMessage }}
</p>
</VControl>
</VField>
<VField id="promitional">
<VControl class="setting-item">
<VCheckbox
color="primary"
:label="t('auth.label.promotional')"
paddingless
/>
</VControl>
</VField>
<!-- Submit -->
<div class="login">
<VButton
type="submit"
color="primary"
bold
fullwidth
raised
>
{{ t('auth.action.signup') }}
</VButton>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Image section (hidden on mobile) -->
<div class="column login-column is-7 is-hidden-mobile hero-banner">
<div class="hero login-hero is-fullheight is-app-grey">
<div class="hero-body is-justify-content-center is-fullwidth">
<div class="is-flex is-justify-content-center is-fullwidth">
<img
class="light-image has-light-shadow has-light-border"
src="/images/illustrations/apps/vuero-banking-light.webp"
alt=""
>
<img
class="dark-image has-light-shadow"
src="/images/illustrations/apps/vuero-banking-dark.webp"
alt=""
>
</div>
</div>
</div>
</div>
</div>
</template>

133
src/pages/auth/signup-3.vue Normal file
View File

@@ -0,0 +1,133 @@
<script setup lang="ts">
const router = useRouter()
const notyf = useNotyf()
const isLoading = ref(false)
const handleSignup = async () => {
if (!isLoading.value) {
isLoading.value = true
sleep(2000)
notyf.dismissAll()
notyf.primary('Welcome, Erik Kovalsky')
router.push('/sidebar/dashboards')
isLoading.value = false
}
}
useHead({
title: 'Auth Signup 3 - Vuero',
})
</script>
<template>
<div class="auth-wrapper-inner is-single">
<LandingGrids class="is-contrasted" />
<!--Fake navigation-->
<div class="auth-nav">
<div class="left" />
<div class="center">
<RouterLink
to="/"
class="header-item"
>
<AnimatedLogo
width="38px"
height="38px"
/>
</RouterLink>
</div>
<div class="right">
<VDarkmodeToggle />
</div>
</div>
<!--Single Centered Form-->
<div class="single-form-wrap is-relative">
<div class="inner-wrap">
<!--Form Title-->
<div class="auth-head">
<h2>Join Us Now.</h2>
<p>Start by creating your account</p>
<RouterLink to="/auth/login-3">
I already have an account
</RouterLink>
</div>
<!--Form-->
<div class="form-card">
<form
method="post"
novalidate
@submit.prevent="handleSignup"
>
<div class="login-form">
<!-- Input -->
<VField>
<VControl icon="lucide:user">
<VInput
type="text"
placeholder="Name"
autocomplete="name"
/>
</VControl>
</VField>
<!-- Input -->
<VField>
<VControl icon="lucide:mail">
<VInput
type="text"
placeholder="Email Address"
autocomplete="email"
/>
</VControl>
</VField>
<!-- Input -->
<VField>
<VControl icon="lucide:lock">
<VInput
type="password"
placeholder="Password"
autocomplete="new-password"
/>
</VControl>
</VField>
<!-- Input -->
<VField>
<VControl icon="lucide:lock">
<VInput
type="password"
placeholder="Repeat Password"
/>
</VControl>
</VField>
<VField>
<VControl class="setting-item">
<VCheckbox
label="Receive promotional offers"
color="primary"
paddingless
/>
</VControl>
</VField>
<!-- Submit -->
<div class="login">
<VButton
color="primary"
type="submit"
bold
fullwidth
raised
>
Sign Up
</VButton>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</template>

View File

107
src/pages/components.vue Normal file
View File

@@ -0,0 +1,107 @@
<script setup lang="ts">
import Layout from '/@src/layouts/sidebar.vue'
const layoutSwitcher = useLayoutSwitcher()
</script>
<template>
<Layout
:theme="layoutSwitcher.sidebarLayoutTheme"
open-on-mounted
default-sidebar="components"
>
<!-- Content Wrapper -->
<RouterView v-slot="{ Component }">
<Transition
name="translate-page-y"
mode="out-in"
>
<component :is="Component" />
</Transition>
</RouterView>
</Layout>
</template>
<style lang="scss">
.demo-s-card,
.demo-r-card,
.demo-l-card {
.title {
margin-bottom: 6px;
}
}
.demo-spacer {
padding-bottom: 100px;
}
.demo-editor-container {
max-width: 740px;
margin: 0 auto;
}
.helper-table {
.category,
th {
width: 25%;
font-family: var(--font-alt);
font-weight: 600;
font-size: 1.05rem;
}
.name {
width: 25%;
}
.description {
width: 50%;
}
}
.demo-table {
.table {
th {
font-family: var(--font-alt);
font-weight: 600;
font-size: 0.9rem;
}
}
}
.demo-field {
max-width: 340px;
}
@media only screen and (width >= 1024px) and (orientation: portrait) {
.landing-page-wrapper .hero .navbar {
&:not(.is-docked) {
.navbar-menu {
padding-top: 0 !important;
padding-bottom: 0 !important;
border: none !important;
.navbar-item {
&.is-theme-toggle {
display: flex;
align-items: center;
justify-content: center;
}
}
}
}
.navbar-brand {
width: auto !important;
}
.navbar-menu {
width: auto;
position: static;
.navbar-end {
margin-inline-end: -28px;
}
}
}
}
</style>

View File

@@ -0,0 +1,89 @@
<script setup lang="ts">
import { VAccordionImageMeta } from '/@src/data/documentation/components-meta'
const data = [
{
image: 'https://source.unsplash.com/FV3GConVSss/1600x900',
title: 'Office Part I',
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit',
},
{
image: 'https://source.unsplash.com/rRiAzFkJPMo/1600x900',
title: 'Office Part II',
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit',
},
{
image: 'https://source.unsplash.com/tvleqH3p1os/1600x900',
title: '12 Great Landscapes',
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit',
},
{
image: 'https://source.unsplash.com/-Xv7k95vOFA/1600x900',
title: 'Team Meetup',
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit',
},
{
image: 'https://source.unsplash.com/F6NvgzU3RfM/1600x900',
title: 'Purple Shades',
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit',
},
{
image: 'https://source.unsplash.com/5E5N49RWtbA/1600x900',
title: 'Blue Note',
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit',
},
]
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VAccordionImage'
})
useHead({
title: 'VAccordionImage - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'Accordions',
},
{
label: 'VAccordionImage',
to: '/components/accordion/image',
},
]"
/>
<div class="columns is-multiline">
<div class="column is-12">
<!--Image Accordion-->
<AccordionImageDocumentation />
</div>
<div class="column is-12">
<VAccordionImage :items="data" />
</div>
<div class="column is-12">
<DocumentationMeta
name="VAccordionImage"
:meta="VAccordionImageMeta"
/>
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,93 @@
<script setup lang="ts">
import { VAccordionMeta } from '/@src/data/documentation/components-meta'
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VAccordion'
})
useHead({
title: 'VAccordion - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'Accordions',
},
{
label: 'V-Accordion',
to: '/components/accordion/',
},
]"
/>
<div class="columns is-multiline">
<div class="column is-12">
<!--Accordion-->
<AccordionDefaultDocumentation />
</div>
<div class="column is-6 is-full-tablet">
<VAccordion
:items="[
{
title: 'Accordion Item 1',
content: 'Sed ut perspiciatis unde omnis iste ...',
},
{
title: 'Accordion Item 2',
content: 'Sed ut perspiciatis unde omnis iste ...',
},
{
title: 'Accordion Item 3',
content: 'Sed ut perspiciatis unde omnis iste ...',
},
]"
/>
</div>
<div class="column is-6 is-full-tablet">
<VAccordion
:items="[
{
title: 'Accordion Item 1',
content: 'Sed ut perspiciatis unde omnis iste ...',
},
{
title: 'Accordion Item 2',
content: 'Sed ut perspiciatis unde omnis iste ...',
},
{
title: 'Accordion Item 3',
content: 'Sed ut perspiciatis unde omnis iste ...',
},
]"
exclusive
/>
</div>
<div class="column is-12 mt-5">
<DocumentationMeta
name="VAccordion"
:meta="VAccordionMeta"
/>
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,65 @@
<script setup lang="ts">
import { VActionMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VAction'
})
useHead({
title: 'VAction - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'Buttons',
},
{
label: 'VAction',
to: '/components/action',
},
]"
/>
<div class="columns">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<!--VAction-->
<VActionDocumentation />
<DocumentationMeta
name="VAction"
:meta="VActionMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,67 @@
<script setup lang="ts">
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VCalendar'
})
useHead({
title: 'VCalendar - Addons - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'Addons',
},
{
label: 'VCalendar',
to: '/components/addons/calendar',
},
]"
/>
<div class="columns is-multiline">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<!--Datepicker-->
<DatepickerBaseDocumentation />
<!--DateRangepicker-->
<DatepickerRangeDocumentation />
<!--DateTimepicker-->
<DatepickerTimeDocumentation />
<!--Timepicker-->
<DatepickerTimeSingleDocumentation />
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,117 @@
<script setup lang="ts">
import type { EditorConfig } from '@ckeditor/ckeditor5-core'
// augment EditorConfig with fonts options
import type {} from '@ckeditor/ckeditor5-font'
const editor = shallowRef<any>()
const CKEditor = defineAsyncComponent(() =>
import('@ckeditor/ckeditor5-vue').then(m => m.default.component),
)
const editorConfig = {
fontFamily: {
options: ['"Montserrat Variable", sans-serif', '"Roboto Flex Variable", sans-serif'],
},
} satisfies EditorConfig
const editorData = ref(`
<h2>The three greatest things you learn from travelling</h2>
<p>Like all the great things on earth travelling teaches us by example. Here are some of the most precious lessons Ive learned over the years of travelling.</p>
<figure class="image image-style-side"><img src="https://images.pexels.com/photos/2335126/pexels-photo-2335126.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940" alt="A lone wanderer looking at Mount Bromo volcano in Indonesia.">
<figcaption>Leaving your comfort zone might lead you to such beautiful sceneries like this one.</figcaption>
</figure>
<h3>Appreciation of diversity</h3>
<p>Getting used to an entirely different culture can be challenging. While its also nice to learn about cultures online or from books, nothing comes close to experiencing cultural diversity in person. You learn to appreciate each and every single one of the differences while you become more culturally fluid.</p>
<blockquote>
<p>The real voyage of discovery consists not in seeking new landscapes, but having new eyes.</p>
<p><strong>Marcel Proust</strong></p>
</blockquote>
<h3>Improvisation</h3>
<p>Life doesn't allow us to execute every single plan perfectly. This especially seems to be the case when you travel. You plan it down to every minute with a big checklist; but when it comes to executing it, something always comes up, and youre left with your improvising skills. You learn to adapt as you go. Heres how my travel checklist looks now:</p>
<ul>
<li>buy the ticket</li>
<li>start your adventure</li>
</ul>
<figure class="image image-style-side"><img src="https://images.pexels.com/photos/2967596/pexels-photo-2967596.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500" alt="Three Monks walking on ancient temple.">
<figcaption>Leaving your comfort zone might lead you to such beautiful sceneries like this one.</figcaption>
</figure>
<h3>Confidence</h3>
<p>Going to a new place can be quite terrifying. While change and uncertainty makes us scared, travelling teaches us how ridiculous it is to be afraid of something before it happens. The moment you face your fear and see there was nothing to be afraid of, is the moment you discover bliss.</p>
`)
onMounted(async () => {
// lazy load the editor when the component is mounted
editor.value = await import('@ckeditor/ckeditor5-build-classic').then(m => m.default)
})
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'CKEditor'
})
useHead({
title: 'CKEditor - Addons - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'Addons',
},
{
label: 'CKEditor',
to: '/components/addons/ckeditor',
},
]"
/>
<div class="columns is-multiline">
<div class="column is-12">
<!--Simple Datatable-->
<CKEditorBasicDocumentation />
<div class="columns">
<div class="column is-12 content">
<CKEditor
v-if="editor"
v-model="editorData"
:editor="editor"
:config="editorConfig"
/>
<VPlaceload
v-else
height="500px"
/>
</div>
</div>
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,64 @@
<script setup lang="ts">
import { VCreditCardMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VCreditCard'
})
useHead({
title: 'VCreditCard - Addons - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'Addons',
},
{
label: 'VCreditCard',
to: '/components/addons/credit-card',
},
]"
/>
<div class="columns is-multiline">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<VCreditCardDocumentation />
<DocumentationMeta
name="VCreditCard"
:meta="VCreditCardMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,64 @@
<script setup lang="ts">
import { VFilePondMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VFilePond'
})
useHead({
title: 'VFilePond - Addons - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'Addons',
},
{
label: 'VFilePond',
to: '/components/addons/filepond',
},
]"
/>
<div class="columns is-multiline">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<VFilepondDocumentation />
<DocumentationMeta
name="VFilePond"
:meta="VFilePondMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,64 @@
<script setup lang="ts">
import { VIMaskInputMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VIMaskInput'
})
useHead({
title: 'VIMaskInput - Addons - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'Addons',
},
{
label: 'VIMaskInput',
to: '/components/addons/imask-input',
},
]"
/>
<div class="columns is-multiline">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<VImaskInputDocumentation />
<DocumentationMeta
name="VIMaskInput"
:meta="VIMaskInputMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,99 @@
<script setup lang="ts">
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'vueform/multiselect'
})
useHead({
title: 'vueform/multiselect - Addons - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'Addons',
},
{
label: 'vueform/multiselect',
to: '/components/addons/vueform-multiselect',
},
]"
/>
<div class="columns is-multiline">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<!--Autocomplete-->
<MultiselectAutocompleteDocumentation />
<!--Simple select-->
<MultiselectBaseDocumentation />
<!--Multiselect-->
<MultiselectMultipleObjectDocumentation />
<!--Multiselect custom label-->
<MultiselectMultipleCustomLabelDocumentation />
<!--Disabled option-->
<MultiselectDisabledOptionDocumentation />
<!--Tags-->
<MultiselectTagsBaseDocumentation />
<!--Slot-->
<MultiselectSelectSlotDocumentation />
<!--Slot search-->
<MultiselectSelectSlotSearchDocumentation />
<!--Slot search-->
<MultiselectSelectSlotUsersDocumentation />
<!--Slot icons search-->
<MultiselectSelectSlotIconsDocumentation />
<!--Tags images-->
<MultiselectTagsImageSlotDocumentation />
<!--Tags users-->
<MultiselectTagsCustomSlotDocumentation />
<!--Tags images stacked-->
<MultiselectTagsImagesStackedDocumentation />
<!--Tags users stacked-->
<MultiselectTagsUsersStackedDocumentation />
<div class="pb-6 mb-6" />
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,78 @@
<script setup lang="ts">
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'vueform/slider'
})
useHead({
title: 'vueform/slider - Addons - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'Addons',
},
{
label: 'vueform/slider',
to: '/components/addons/vueform-slider',
},
]"
/>
<div class="columns is-multiline">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<!--Simple slider-->
<SliderBaseDocumentation />
<!--Squared tooltip-->
<SliderSquaredDocumentation />
<!--Curved tooltip-->
<SliderCurvedDocumentation />
<!--Slider colors-->
<SliderColorDocumentation />
<!--Multiple sliders-->
<SliderMultipleDocumentation />
<!--Tooltip format-->
<SliderFormatDocumentation />
<!--Slider Mergin-->
<SliderMergingDocumentation />
<div class="pb-6 mb-6" />
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,65 @@
<script setup lang="ts">
import { VAnimatedCheckboxMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VAnimatedCheckbox'
})
useHead({
title: 'VAnimatedCheckbox - Switches Elements - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'Switches',
},
{
label: 'VAnimatedCheckbox',
to: '/components/animated-checkbox',
},
]"
/>
<div class="columns">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<!-- @TODO -->
<AnimatedCheckboxDocumentation />
<DocumentationMeta
name="VAnimatedCheckbox"
:meta="VAnimatedCheckboxMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,95 @@
<script setup lang="ts">
import { VAvatarMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VAvatar'
})
useHead({
title: 'VAvatar - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'VAvatar',
to: '/components/avatar/',
},
]"
/>
<div class="columns">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<!--Avatar-->
<AvatarDefaultDocumentation />
<!--Avatar Square-->
<AvatarSquareDocumentation />
<!--Avatar Fallback-->
<AvatarHandleFallbackDocumentation />
<!--Avatar Dot-->
<AvatarDotDocumentation />
<!--Avatar Dot Squared-->
<AvatarDotSquaredDocumentation />
<!--Avatar Dot Colors-->
<AvatarDotColorsDocumentation />
<!--Avatar Badge-->
<AvatarBadgeDocumentation />
<!--Avatar Fake-->
<AvatarFakeDocumentation />
<!--Avatar Fake Squared-->
<AvatarFakeSquareDocumentation />
<!--Avatar Fake Badge-->
<AvatarFakeBadgeDocumentation />
<!--Avatar Fake Colors-->
<AvatarFakeColorDocumentation />
<!--Avatar Fake Squared Colors-->
<AvatarFakeSquaredColorDocumentation />
<DocumentationMeta
name="VAvatar"
:meta="VAvatarMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,68 @@
<script setup lang="ts">
import { VAvatarStackMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VAvatarStack'
})
useHead({
title: 'VAvatarStack - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'VAvatarStack',
to: '/components/avatar/stack',
},
]"
/>
<div class="columns">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<!--Small Stack-->
<AvatarStackSmallDocumentation />
<!--Regular Stack-->
<AvatarStackRegularDocumentation />
<!--Medium Stack-->
<AvatarStackMediumDocumentation />
<DocumentationMeta
name="VAvatarStack"
:meta="VAvatarStackMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,77 @@
<script setup lang="ts">
import { VBlockMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VBlock'
})
useHead({
title: 'VBlock - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'VBlock',
to: '/components/block',
},
]"
/>
<div class="columns">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<!--VBlock base-->
<VBlockBaseDocumentation />
<!--VBlock icon-->
<VBlockIconDocumentation />
<!--VBlock center-->
<VBlockCenterDocumentation />
<!--VBlock icon center-->
<VBlockIconCenterDocumentation />
<!--VBlock base responsive-->
<VBlockBaseResponsiveDocumentation />
<!--VBlock center responsive-->
<VBlockCenterResponsiveDocumentation />
<DocumentationMeta
name="VBlock"
:meta="VBlockMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,74 @@
<script setup lang="ts">
import { VBreadcrumbMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VBreadcrumb'
})
useHead({
title: 'VBreadcrumb - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'VBreadcrumb',
to: '/components/breadcrumb',
},
]"
/>
<div class="columns">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<!--Breadcrumb Default-->
<BreadcrumbDefaultDocumentation />
<!--Arrow Separator-->
<BreadcrumbArrowDocumentation />
<!--Bullet Separator-->
<BreadcrumbBulletDocumentation />
<!--Dot Separator-->
<BreadcrumbDotDocumentation />
<!--Succeeds Separator-->
<BreadcrumbSucceedsDocumentation />
<DocumentationMeta
name="VBreadcrumb"
:meta="VBreadcrumbMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,103 @@
<script setup lang="ts">
import { VButtonMeta, VButtonsMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VButton'
})
useHead({
title: 'VButton - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'Buttons',
},
{
label: 'VButton',
to: '/components/button',
},
]"
/>
<div class="columns">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<!--VButton base-->
<VButtonBaseDocumentation />
<!--VButton link-->
<VButtonLinkDocumentation />
<!--VButton colors-->
<VButtonColorsDocumentation />
<!--VButton light colors-->
<VButtonColorsLightDocumentation />
<!--VButton outlined colors-->
<VButtonColorsOutlinedDocumentation />
<!--VButton placeload-->
<VButtonPlaceloadDocumentation />
<!--VButton Elevation-->
<VButtonElevatedDocumentation />
<!--VButton Disabled-->
<VButtonDisabledDocumentation />
<!--VButton Font Awesome-->
<VButtonFaDocumentation />
<!--VButton Feather-->
<VButtonFeatherDocumentation />
<!--VButton Group-->
<VButtonGroupDocumentation />
<!--VButton Addons-->
<VButtonAddonsDocumentation />
<DocumentationMeta
name="VButton"
:meta="VButtonMeta"
/>
<DocumentationMeta
name="VButtons"
:meta="VButtonsMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,169 @@
<script setup lang="ts">
import { VCardActionMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VCardAction'
})
useHead({
title: 'VCardAction - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'VCardAction',
to: '/components/card/action',
},
]"
/>
<div class="columns is-multiline">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<VCardActionDocumentation />
<div class="columns is-multiline mb-6">
<div class="column is-4">
<VCardAction
avatar="https://media.cssninja.io/content/avatars/19.jpg"
badge="/images/icons/flags/germany.svg"
title="Greta K."
subtitle="Sales Manager"
>
<template #action>
<VTag
color="green"
label="trending"
rounded
/>
</template>
</VCardAction>
</div>
<div class="column is-4">
<VCardAction
avatar="https://media.cssninja.io/content/avatars/18.jpg"
badge="/images/icons/flags/united-states-of-america.svg"
title="George W."
subtitle="Software Engineer"
>
<template #action>
<VIconButton
icon="lucide:heart"
circle
/>
</template>
</VCardAction>
</div>
<div class="column is-4">
<VCardAction
avatar="https://media.cssninja.io/content/avatars/22.jpg"
badge="/images/icons/flags/france.svg"
title="Sandrine C."
subtitle="HR Manager"
radius="rounded"
>
<template #action>
<WidgetDropdown />
</template>
</VCardAction>
</div>
</div>
<VCardActionContentDocumentation />
<div class="columns is-multiline mb-6">
<div class="column is-4">
<VCardAction
avatar="https://media.cssninja.io/content/avatars/19.jpg"
badge="/images/icons/flags/germany.svg"
title="Greta K."
subtitle="Sales Manager"
>
<template #action>
<VTag
color="green"
label="trending"
rounded
/>
</template>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quibusnam
praeteritis? At multis se probavit. Quoniam, si dis placet, ab Epicuro
loqui discimus. Et ille ridens.
</p>
</VCardAction>
</div>
<div class="column is-4">
<VCardAction
avatar="https://media.cssninja.io/content/avatars/18.jpg"
badge="/images/icons/flags/united-states-of-america.svg"
title="George W."
subtitle="Software Engineer"
>
<template #action>
<VAction>View</VAction>
</template>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quibusnam
praeteritis? At multis se probavit. Quoniam, si dis placet, ab Epicuro
loqui discimus. Et ille ridens.
</p>
</VCardAction>
</div>
<div class="column is-4">
<VCardAction
avatar="https://media.cssninja.io/content/avatars/22.jpg"
badge="/images/icons/flags/france.svg"
title="Sandrine C."
subtitle="HR Manager"
radius="rounded"
>
<template #action>
<WidgetDropdown />
</template>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quibusnam
praeteritis? At multis se probavit. Quoniam, si dis placet, ab Epicuro
loqui discimus. Et ille ridens.
</p>
</VCardAction>
</div>
</div>
<DocumentationMeta
name="VCardAction"
:meta="VCardActionMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,199 @@
<script setup lang="ts">
import type { VAvatarProps } from '/@src/components/base/VAvatar.vue'
import * as userStacks from '/@src/data/users/userStacks'
import { VCardAdvancedMeta } from '/@src/data/documentation/components-meta'
const userStack2 = userStacks.userStack2 as VAvatarProps[]
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VCardAdvanced'
})
useHead({
title: 'VCardAdvanced - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'Cards',
},
{
label: 'VCardAdvanced',
to: '/components/card/advanced',
},
]"
/>
<div class="columns is-multiline">
<div class="column is-12">
<!--VCard Advanced-->
<VCardAdvancedDocumentation />
</div>
<div class="column is-4">
<VCardAdvanced>
<template #header-left>
<VBlock
title="Anna B."
subtitle="UX Designer"
center
>
<template #icon>
<VAvatar
picture="https://media.cssninja.io/content/avatars/19.jpg"
badge="/images/icons/flags/germany.svg"
/>
</template>
</VBlock>
</template>
<template #header-right>
<VAvatarStack
:avatars="userStack2"
:limit="1"
size="small"
/>
</template>
<template #content>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quibusnam
praeteritis? At multis se probavit. Quoniam, si dis placet, ab Epicuro loqui
discimus. Et ille ridens.
</p>
</template>
<template #footer-left>
<div class="tags">
<VTag
label="Business"
color="solid"
rounded
/>
</div>
</template>
<template #footer-right>
<VButton
color="primary"
raised
>
Action
</VButton>
</template>
</VCardAdvanced>
</div>
<div class="column is-4">
<VCardAdvanced radius="smooth">
<template #header-left>
<div class="tags">
<VTag
label="Business"
color="solid"
rounded
/>
</div>
</template>
<template #header-right>
<VButton
color="primary"
raised
>
Action
</VButton>
</template>
<template #content>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quibusnam
praeteritis? At multis se probavit. Quoniam, si dis placet, ab Epicuro loqui
discimus. Et ille ridens.
</p>
</template>
<template #footer-left>
<VBlock
title="Anna B."
subtitle="UX Designer"
center
>
<template #icon>
<VAvatar
picture="https://media.cssninja.io/content/avatars/19.jpg"
badge="/images/icons/flags/germany.svg"
/>
</template>
</VBlock>
</template>
<template #footer-right>
<VAvatarStack
:avatars="userStack2"
:limit="1"
size="small"
/>
</template>
</VCardAdvanced>
</div>
<div class="column is-4">
<VCardAdvanced radius="rounded">
<template #header-left>
<h3 class="title is-6">
A Card Title
</h3>
</template>
<template #header-right>
<WidgetDropdown />
</template>
<template #content>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quibusnam
praeteritis? At multis se probavit. Quoniam, si dis placet, ab Epicuro loqui
discimus. Et ille ridens.
</p>
</template>
<template #footer-left>
<VBlock
title="Anna B."
subtitle="UX Designer"
center
>
<template #icon>
<VAvatar
picture="https://media.cssninja.io/content/avatars/19.jpg"
badge="/images/icons/flags/germany.svg"
/>
</template>
</VBlock>
</template>
<template #footer-right>
<VAvatarStack
:avatars="userStack2"
:limit="1"
size="small"
/>
</template>
</VCardAdvanced>
</div>
<div class="column is-12 mt-5">
<DocumentationMeta
name="VCardAdvanced"
:meta="VCardAdvancedMeta"
/>
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,352 @@
<script setup lang="ts">
import { popovers } from '/@src/data/users/userPopovers'
import { VCardMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VCard'
})
useHead({
title: 'VCard - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'VCard',
to: '/components/card/',
},
]"
/>
<div class="columns is-multiline">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<!--Basic Cards-->
<CardBaseDocumentation />
<div class="columns is-multiline mb-6">
<div class="column is-4">
<VCard radius="smooth">
<h3 class="title is-5 mb-2">
I have smooth radius
</h3>
<p>
I can be used as is in any layout. VCards are simple containers that can
hold any type of content.
</p>
</VCard>
</div>
<div class="column is-4">
<VCard>
<h3 class="title is-5 mb-2">
I have regular radius
</h3>
<p>
I can be used as is in any layout. VCards are simple containers that can
hold any type of content.
</p>
</VCard>
</div>
<div class="column is-4">
<VCard radius="rounded">
<h3 class="title is-5 mb-2">
I have rounded radius
</h3>
<p>
I can be used as is in any layout. VCards are simple containers that can
hold any type of content.
</p>
</VCard>
</div>
</div>
<!--Elevated Cards-->
<CardElevatedDocumentation />
<div class="columns is-multiline mb-6">
<div class="column is-4 mb-2">
<VCard
radius="smooth"
elevated
>
<h3 class="title is-5 mb-2">
Raised VCard
</h3>
<p>
I can be used as is in any layout. VCards are simple containers that can
hold any type of content.
</p>
</VCard>
</div>
<div class="column is-4">
<VCard elevated>
<h3 class="title is-5 mb-2">
Raised VCard
</h3>
<p>
I can be used as is in any layout. VCards are simple containers that can
hold any type of content.
</p>
</VCard>
</div>
<div class="column is-4">
<VCard
radius="rounded"
elevated
>
<h3 class="title is-5 mb-2">
Raised VCard
</h3>
<p>
I can be used as is in any layout. VCards are simple containers that can
hold any type of content.
</p>
</VCard>
</div>
</div>
<!--Colored Cards-->
<CardColorsDocumentation />
<div class="columns is-multiline mb-6">
<div class="column is-4">
<VCard
radius="smooth"
color="primary"
>
<h3 class="title is-5 mb-2">
Primary VCard
</h3>
<p>
I can be used as is in any layout. VCards are simple containers that can
hold any type of content.
</p>
</VCard>
</div>
<div class="column is-4">
<VCard color="secondary">
<h3 class="title is-5 mb-2">
Secondary VCard
</h3>
<p>
I can be used as is in any layout. VCards are simple containers that can
hold any type of content.
</p>
</VCard>
</div>
<div class="column is-4">
<VCard
radius="rounded"
color="info"
>
<h3 class="title is-5 mb-2">
Info VCard
</h3>
<p>
I can be used as is in any layout. VCards are simple containers that can
hold any type of content.
</p>
</VCard>
</div>
<div class="column is-4">
<VCard
radius="smooth"
color="success"
>
<h3 class="title is-5 mb-2">
Success VCard
</h3>
<p>
I can be used as is in any layout. VCards are simple containers that can
hold any type of content.
</p>
</VCard>
</div>
<div class="column is-4">
<VCard color="warning">
<h3 class="title is-5 mb-2">
Warning VCard
</h3>
<p>
I can be used as is in any layout. VCards are simple containers that can
hold any type of content.
</p>
</VCard>
</div>
<div class="column is-4">
<VCard
radius="rounded"
color="danger"
>
<h3 class="title is-5 mb-2">
Danger VCard
</h3>
<p>
I can be used as is in any layout. VCards are simple containers that can
hold any type of content.
</p>
</VCard>
</div>
</div>
<!--Structured Cards-->
<CardStructuredDocumentation />
<div class="columns is-multiline mb-6">
<div class="column is-4">
<VCard
radius="smooth"
elevated
>
<div class="card-head">
<VBlock
title="Greta K."
subtitle="Sales Manager"
center
class="no-margin"
>
<template #icon>
<Tippy
class="has-help-cursor"
interactive
:offset="[0, 10]"
placement="top-start"
>
<VAvatar
picture="https://media.cssninja.io/content/avatars/19.jpg"
badge="/images/icons/flags/germany.svg"
/>
<template #content>
<UserPopoverContent :user="popovers.user19" />
</template>
</Tippy>
</template>
</VBlock>
<UserCardDropdown />
</div>
<div class="card-inner">
<p>
I can be used as is in any layout. VCards are simple containers that can
hold any type of content.
</p>
</div>
</VCard>
</div>
<div class="column is-4">
<VCard elevated>
<div class="card-head">
<VBlock
title="Greta K."
subtitle="Sales Manager"
center
class="no-margin"
>
<template #icon>
<Tippy
class="has-help-cursor"
interactive
:offset="[0, 10]"
placement="top-start"
>
<VAvatar
picture="https://media.cssninja.io/content/avatars/19.jpg"
badge="/images/icons/flags/germany.svg"
/>
<template #content>
<UserPopoverContent :user="popovers.user19" />
</template>
</Tippy>
</template>
</VBlock>
<UserCardDropdown />
</div>
<div class="card-inner">
<p>
I can be used as is in any layout. VCards are simple containers that can
hold any type of content.
</p>
</div>
</VCard>
</div>
<div class="column is-4">
<VCard
radius="rounded"
elevated
>
<div class="card-head">
<VBlock
title="Greta K."
subtitle="Sales Manager"
center
class="no-margin"
>
<template #icon>
<Tippy
class="has-help-cursor"
interactive
:offset="[0, 10]"
placement="top-start"
>
<VAvatar
picture="https://media.cssninja.io/content/avatars/19.jpg"
badge="/images/icons/flags/germany.svg"
/>
<template #content>
<UserPopoverContent :user="popovers.user19" />
</template>
</Tippy>
</template>
</VBlock>
<UserCardDropdown />
</div>
<div class="card-inner">
<p>
I can be used as is in any layout. VCards are simple containers that can
hold any type of content.
</p>
</div>
</VCard>
</div>
</div>
<DocumentationMeta
name="VCard"
:meta="VCardMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,117 @@
<script setup lang="ts">
import { VCardMediaMeta } from '/@src/data/documentation/components-meta'
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VCardMedia'
})
useHead({
title: 'VCardMedia - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'Cards',
},
{
label: 'VCardMedia',
to: '/components/card/media',
},
]"
/>
<div class="columns is-multiline">
<div class="column is-12">
<!--VCardMedia-->
<VCardMediaDocumentation />
</div>
<div class="column is-4">
<VCardMedia
image="https://media.cssninja.io/content/photos/apps/1.jpg"
avatar="https://media.cssninja.io/content/avatars/19.jpg"
badge="/images/icons/flags/germany.svg"
title="Greta K."
subtitle="Sales Manager"
>
<p class="pb-4">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ego vero isti,
inquam, permitto. Id Sextilius factum negabat. Apparet statim, quae sint
officia, quae actiones. Sed hoc sane concedamus...
</p>
<a
class="action-link"
tabindex="0"
>Read More</a>
</VCardMedia>
</div>
<div class="column is-4">
<VCardMedia
image="https://media.cssninja.io/content/photos/apps/2.png"
avatar="https://media.cssninja.io/content/avatars/23.jpg"
badge="/images/icons/flags/united-states-of-america.svg"
title="Irina V."
subtitle="Project Manager"
>
<p class="pb-4">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ego vero isti,
inquam, permitto. Id Sextilius factum negabat. Apparet statim, quae sint
officia, quae actiones. Sed hoc sane concedamus...
</p>
<a
class="action-link"
tabindex="0"
>Read More</a>
</VCardMedia>
</div>
<div class="column is-4">
<VCardMedia
image="https://media.cssninja.io/content/photos/apps/3.png"
avatar="https://media.cssninja.io/content/avatars/12.jpg"
badge="/images/icons/flags/united-states-of-america.svg"
title="Joshua S."
subtitle="Backend Developer"
>
<p class="pb-4">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ego vero isti,
inquam, permitto. Id Sextilius factum negabat. Apparet statim, quae sint
officia, quae actiones. Sed hoc sane concedamus...
</p>
<a
class="action-link"
tabindex="0"
>Read More</a>
</VCardMedia>
</div>
<div class="column is-12 mt-5">
<DocumentationMeta
name="VCardMedia"
:meta="VCardMediaMeta"
/>
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,103 @@
<script setup lang="ts">
import { VCardSocialMeta } from '/@src/data/documentation/components-meta'
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VCardSocial'
})
useHead({
title: 'VCardSocial - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'Cards',
},
{
label: 'VCardSocial',
to: '/components/card/social',
},
]"
/>
<div class="columns is-multiline">
<div class="column is-12">
<!--VCard Social-->
<VCardSocialDocumentation />
</div>
<div class="column is-4">
<VCardSocial
title="Featured Tweet"
network="twitter"
avatar="https://media.cssninja.io/content/avatars/19.jpg"
username="@gretak"
:hashtags="['#bulmaio', '#css', '#responsive']"
share-label="Retweet"
like-label="Save"
>
<span class="dark-inverted">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus nec iaculis
mauris.
</span>
</VCardSocial>
</div>
<div class="column is-4">
<VCardSocial
title="Featured Story"
network="instagram"
avatar="https://media.cssninja.io/content/avatars/5.jpg"
username="Mary L."
:hashtags="['#bulmaio', '#css', '#responsive']"
>
<span class="dark-inverted">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus nec iaculis
mauris.
</span>
</VCardSocial>
</div>
<div class="column is-4">
<VCardSocial
title="Featured Post"
network="facebook"
avatar="https://media.cssninja.io/content/avatars/36.jpg"
username="Benoit L."
:hashtags="['#bulmaio', '#css', '#responsive']"
>
<template #default>
<span class="dark-inverted">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus nec
iaculis mauris.
</span>
</template>
</VCardSocial>
</div>
<div class="column is-12 mt-5">
<DocumentationMeta
name="VCardSocial"
:meta="VCardSocialMeta"
/>
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,71 @@
<script setup lang="ts">
import { VCheckboxMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VCheckbox'
})
useHead({
title: 'VCheckbox - Forms Elements - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'Switches',
},
{
label: 'VCheckbox',
to: '/components/checkbox',
},
]"
/>
<div class="columns">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<!-- VCheckbox -->
<CheckboxOutlinedDocumentation />
<CheckboxOutlinedCircleDocumentation />
<CheckboxSolidDocumentation />
<CheckboxSolidCircleDocumentation />
<DocumentationMeta
name="VCheckbox"
:meta="VCheckboxMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,87 @@
<script setup lang="ts">
import {
VCollapseMeta,
VCollapseModelMeta,
} from '/@src/data/documentation/components-meta'
const data = [
{
title: 'Accordion Item 1',
content: 'Sed ut perspiciatis unde omnis iste ...',
},
{
title: 'Accordion Item 2',
content: 'Sed ut perspiciatis unde omnis iste ...',
},
{
title: 'Accordion Item 3',
content: 'Sed ut perspiciatis unde omnis iste ...',
},
]
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VCollapse'
})
useHead({
title: 'VCollapse - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'Accordions',
},
{
label: 'VCollapse',
to: '/components/collapse',
},
]"
/>
<div class="columns is-multiline">
<div class="column is-12">
<!--Collapse-->
<AccordionCollapseDocumentation />
</div>
<div class="column is-6">
<VCollapse :items="data" />
</div>
<div class="column is-6">
<VCollapse
:items="data"
with-chevron
/>
</div>
<div class="column is-12">
<DocumentationMeta
name="VCollapse"
:meta="VCollapseMeta"
/>
<DocumentationMeta
name="VCollapseModel"
:meta="VCollapseModelMeta"
/>
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,114 @@
<script setup lang="ts">
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'Colors'
})
useHead({
title: 'Colors - Components - Vuero',
})
const bulmaColors = [
'primary',
'secondary',
'danger',
'success',
'info',
'warning',
'dark',
'link',
]
const additionalColors = [
'purple',
'blue',
'red',
'orange',
'yellow',
'pink',
'green',
'lime',
]
const layoutColors = [
'dark-sidebar',
'body-color',
'background-grey',
// 'medium-grey',
// 'light-grey',
// 'primary-grey',
// 'muted-grey',
'fade-grey',
// 'widget-grey',
// 'smoke-white',
'white',
// 'border',
// 'placeholder',
'light-text',
]
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'Colors',
to: '/components/colors',
},
]"
/>
<div class="columns is-multiline">
<div class="column is-12">
<!--Theme Colors-->
<ColorsDocumentation />
<div class="columns is-multiline mb-6">
<div
v-for="color in bulmaColors"
:key="color"
class="column is-4"
>
<DocumentationColor :color />
</div>
</div>
<!-- <ThemeDocumentation /> -->
<div class="columns is-multiline mb-6">
<div
v-for="color in layoutColors"
:key="color"
class="column is-4"
>
<DocumentationColor :color />
</div>
</div>
<!-- <ThemingDocumentation /> -->
<div class="columns is-multiline mb-6">
<div
v-for="color in additionalColors"
:key="color"
class="column is-4"
>
<DocumentationColor :color />
</div>
</div>
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,73 @@
<script setup lang="ts">
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'Content'
})
useHead({
title: 'Content - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'Content',
to: '/components/content',
},
]"
/>
<div class="columns">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<!--Typography-->
<ContentUnorderedListsDocumentation />
<!--Typography-->
<ContentOrderedListsDocumentation />
<!--Typography-->
<ContentTitlesDocumentation />
<!--Typography-->
<ContentSubtitlesDocumentation />
<!--Typography-->
<ContentBlockquotesDocumentation />
<!--Typography-->
<ContentDividersDocumentation />
<!--Typography-->
<ContentTablesDocumentation />
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,84 @@
<script setup lang="ts">
import { VDropdownMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
// pageTitle.value = 'VDropdown'
onMounted(() => {
pageTitle.value = 'VDropdown'
})
useHead({
title: 'VDropdown - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'VDropdown',
to: '/components/dropdown',
},
]"
/>
<div class="columns">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<!--Dropdown-->
<DropdownDocumentation />
<!--Dropdown-->
<DropdownColorsDocumentation />
<!--Dropdown-->
<DropdownButtonSlotDocumentation />
<!--Dropdown-->
<DropdownButtonHoverDocumentation />
<!--Dropdown-->
<DropdownModernDocumentation />
<!--Dropdown-->
<DropdownWithIconsDocumentation />
<!--Dropdown-->
<DropdownWithImagesDocumentation />
<DocumentationMeta
name="VDropdown"
:meta="VDropdownMeta"
/>
<!--Artificial Spacing-->
<div class="demo-spacer" />
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,67 @@
<script setup lang="ts">
import { VFlexMeta, VFlexItemMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VFlex'
})
useHead({
title: 'VFlex - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'VFlex',
to: '/components/flex',
},
]"
/>
<div class="columns">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<!--VFlex-->
<VFlexBaseDocumentation />
<DocumentationMeta
name="VFlex"
:meta="VFlexMeta"
/>
<DocumentationMeta
name="VFlexItem"
:meta="VFlexItemMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,273 @@
<script setup lang="ts">
import { flexRowsContacts, flexRowsAdvanced } from '/@src/data/documentation/table'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'Customize columns'
})
useHead({
title: 'Customize columns - VFlexTable - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'VFlexTable',
to: '/components/flextable/',
},
{
label: 'Customize columns',
to: '/components/flextable/columns',
},
]"
/>
<div class="columns">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<VFlexTableColumnsDocumentation />
<div class="mt-4">
<VFlexTable
rounded
:data="flexRowsContacts"
:columns="{
id: {
label: 'Identifier (inverted)',
inverted: true,
format: (value) => `ID-0000${value}`,
},
company: {
label: 'Company (bold)',
bold: true,
},
type: 'Type',
status: {
label: 'Status (center)',
align: 'center',
},
contacts: {
label: 'Contacts (end)',
align: 'end',
format: (value) => value.map((r: any) => r.initials).join(', '),
},
}"
/>
</div>
<DocumentationDivider
id="grow"
title="Grow"
/>
<div class="mt-4">
<VFlexTable
rounded
:data="flexRowsContacts"
:columns="{
id: {
label: 'Identifier',
inverted: true,
format: (value) => `ID-0000${value}`,
},
company: {
label: 'Company',
bold: true,
},
type: 'Type',
status: {
label: 'Status',
align: 'center',
},
contacts: {
label: 'Contacts (grow)',
align: 'end',
grow: true,
format: (value) => value.map((r: any) => r.initials).join(', '),
},
}"
/>
</div>
<DocumentationDivider
id="grow-lg"
title="Grow (large)"
/>
<div class="mt-4">
<VFlexTable
rounded
:data="flexRowsContacts"
:columns="{
id: {
label: 'Identifier',
inverted: true,
format: (value) => `ID-0000${value}`,
},
company: {
label: 'Company',
bold: true,
},
type: 'Type',
status: {
label: 'Status',
align: 'center',
},
contacts: {
label: 'Contacts (grow: lg)',
align: 'end',
grow: 'lg',
format: (value) => value.map((r: any) => r.initials).join(', '),
},
}"
/>
</div>
<DocumentationDivider
id="grow-xl"
title="Grow (xl)"
/>
<div class="mt-4">
<VFlexTable
rounded
:data="flexRowsContacts"
:columns="{
id: {
label: 'Identifier',
inverted: true,
format: (value) => `ID-0000${value}`,
},
company: {
label: 'Company',
bold: true,
},
type: 'Type',
status: {
label: 'Status',
align: 'center',
},
contacts: {
label: 'Contacts (grow: xl)',
align: 'end',
grow: 'xl',
format: (value) => value.map((r: any) => r.initials).join(', '),
},
}"
/>
</div>
<VFlexTableScrollableDocumentation class="mt-6" />
<div class="mt-4">
<VFlexTable
rounded
print-objects
:data="flexRowsContacts"
:columns="{
id: {
label: 'Identifier',
inverted: true,
format: (value) => `ID-0000${value}`,
},
company: {
label: 'Company',
bold: true,
grow: true,
},
contacts: {
label: 'Contacts (scrollX & scrollY)',
grow: true,
scrollX: true,
scrollY: true,
cellClass: 'max-h-280',
},
}"
/>
</div>
<VFlexTableMediaDocumentation class="mt-6" />
<div class="mt-4">
<VFlexTable
rounded
:data="flexRowsAdvanced"
:columns="{
username: {
label: 'User (media)',
grow: true,
media: true,
},
position: 'Position',
status: {
label: 'Status',
},
contacts: {
label: 'Contacts',
align: 'end',
format: (value) => value.map((r: any) => r.initials).join(', '),
},
}"
>
<template #body-cell="{ row, column, value }">
<template v-if="column.key === 'username'">
<VAvatar
size="medium"
:picture="row.picture"
:badge="row.badge"
/>
<div>
<span class="item-name">{{ row.name }}</span>
<span class="item-meta">
<strong>{{ value }}</strong>
</span>
</div>
</template>
<VPlaceload
v-else-if="column.key === 'status'"
mobile-width="30%"
/>
<VAvatarStack
v-else-if="column.key === 'contacts'"
class="is-pushed-mobile"
size="small"
:avatars="row.contacts"
:title="value"
:limit="3"
/>
</template>
</VFlexTable>
</div>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,170 @@
<script setup lang="ts">
import { flexRowsBasic, flexRowsContacts } from '/@src/data/documentation/table'
import { VFlexTableMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const rowClick = (row: any) => {
console.log(row)
}
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VFlexTable'
})
useHead({
title: 'VFlexTable - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'VFlexTable',
to: '/components/flextable/',
},
]"
/>
<div class="columns">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<VFlexTableBaseDocumentation />
<div class="mt-4 mb-4">
<VFlexTable :data="flexRowsBasic" />
</div>
<DocumentationDivider
id="no-header"
title="Without header"
/>
<div class="mt-4">
<VFlexTable
:data="flexRowsBasic"
no-header
/>
</div>
<DocumentationDivider
id="separators"
title="With separators"
/>
<div class="mt-4">
<VFlexTable
:data="flexRowsBasic"
separators
/>
</div>
<DocumentationDivider
id="rounded"
title="Rounded"
/>
<div class="mt-4">
<VFlexTable
:data="flexRowsBasic"
rounded
/>
</div>
<DocumentationDivider
id="compact"
title="Compact"
/>
<div class="mt-4 mb-4">
<VFlexTable
:data="flexRowsBasic"
compact
/>
</div>
<DocumentationDivider
id="all-options"
title="All options together"
/>
<div class="mt-4">
<VFlexTable
:data="flexRowsBasic"
no-header
compact
rounded
separators
clickable
@row-click="rowClick"
/>
</div>
<DocumentationDivider
id="subtable"
title="SubTable"
/>
<div class="mt-4">
<VFlexTable
:data="flexRowsBasic"
subtable
/>
</div>
<VFlexTablePrintObjectsDocumentation class="mt-6" />
<div class="mt-4">
<VFlexTable
rounded
:data="flexRowsContacts"
print-objects
/>
</div>
<DocumentationDivider
id="without-print-object"
title="Without print-objects"
/>
<div class="mt-4">
<VFlexTable
rounded
:data="flexRowsContacts"
/>
</div>
<DocumentationMeta
name="VFlexTable"
:meta="VFlexTableMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,221 @@
<script setup lang="ts">
import { flexRowsAdvanced } from '/@src/data/documentation/table'
import VTag from '/@src/components/base/VTag.vue'
import FlexTableDropdown from '/@src/components/partials/dropdowns/FlexTableDropdown.vue'
import VFlexTableSortColumn from '/@src/components/base/VFlexTableSortColumn.vue'
import VAvatarStack from '/@src/components/base/VAvatarStack.vue'
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'Render functions (advanced)'
})
useHead({
title: 'Render functions (advanced) - VFlexTable - Components - Vuero',
})
const collator = new Intl.Collator('en')
const numberFormat = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
})
const router = useRoute()
const sortedData = computed(() => {
switch (router.query.sort) {
case 'username:asc': {
return [...flexRowsAdvanced].sort((a, b) =>
collator.compare(a.username, b.username),
)
}
case 'username:desc': {
return [...flexRowsAdvanced].sort((a, b) =>
collator.compare(b.username, a.username),
)
}
case 'annual-earnings:asc': {
return [...flexRowsAdvanced].sort((a, b) =>
a.annualEarnings > b.annualEarnings ? 1 : -1,
)
}
case 'annual-earnings:desc': {
return [...flexRowsAdvanced].sort((a, b) =>
a.annualEarnings > b.annualEarnings ? -1 : 1,
)
}
case 'position:asc': {
return [...flexRowsAdvanced].sort((a, b) =>
collator.compare(a.position, b.position),
)
}
case 'position:desc': {
return [...flexRowsAdvanced].sort((a, b) =>
collator.compare(b.position, a.position),
)
}
case 'status:asc': {
return [...flexRowsAdvanced].sort((a, b) => collator.compare(a.status, b.status))
}
case 'status:desc': {
return [...flexRowsAdvanced].sort((a, b) => collator.compare(b.status, a.status))
}
case 'contacts:asc': {
return [...flexRowsAdvanced].sort((a, b) =>
a.contacts.length > b.contacts.length ? 1 : -1,
)
}
case 'contacts:desc': {
return [...flexRowsAdvanced].sort((a, b) =>
a.contacts.length > b.contacts.length ? -1 : 1,
)
}
default: {
return flexRowsAdvanced
}
}
})
// this is the how rows and columns are rendered
const exampleColumns = {
username: {
bold: true,
// we can use custom render function for column heading
renderHeader: () =>
h(VFlexTableSortColumn, {
label: 'Name',
id: 'username',
}),
},
position: {
renderHeader: () =>
h(VFlexTableSortColumn, {
label: 'Position',
id: 'position',
}),
},
annualEarnings: {
inverted: true,
format: (value: any) => numberFormat.format(value),
// we can use custom render function for column heading
renderHeader: () =>
h(VFlexTableSortColumn, {
label: 'Revenue',
id: 'annual-earnings',
}),
},
status: {
label: 'Status',
// we can use custom render function for each rows
renderRow: (row: any) =>
h(
VTag,
{
rounded: true,
color:
row.status === 'Active'
? 'success'
: row.status === 'New'
? 'info'
: row.status === 'Suspended'
? 'orange'
: undefined,
},
// that notation is to render content in the default slot
{
default() {
return `${row.status}`
},
},
),
// we can use custom render function for column heading
renderHeader: () =>
h(VFlexTableSortColumn, {
label: 'Status',
id: 'status',
}),
},
contacts: {
renderHeader: () =>
h(
'span',
{},
h(VFlexTableSortColumn, {
label: 'Contacts',
id: 'contacts',
}),
),
renderRow: (row: any) =>
// We can render custom components and set props
h(VAvatarStack, {
class: 'is-pushed-mobile',
size: 'small',
avatars: row.contacts,
limit: 3,
}),
},
actions: {
label: '',
align: 'end',
renderRow: (row: any) =>
h(FlexTableDropdown, {
// We can catch all events from vue
onView: () => {
console.log('viewing', row)
},
onProjects: () => {
console.log('projects', row)
},
onSchedule: () => {
console.log('schedule', row)
},
onRemove: () => {
console.log('remove', row)
},
}),
},
} as const
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'VFlexTable',
to: '/components/flextable/',
},
{
label: 'Render functions (advanced)',
to: '/components/flextable/render',
},
]"
/>
<div class="columns">
<div class="column is-12">
<!--Flex Table-->
<VFlexTableWithRenderDocumentation class="mt-5" />
<div class="mt-4">
<!--VFlexTabe-->
<VFlexTable
:data="sortedData"
:columns="exampleColumns"
rounded
/>
</div>
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,362 @@
<script setup lang="ts">
import { flexRowsContacts } from '/@src/data/documentation/table'
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'Slots usage'
})
useHead({
title: 'Slots usage - VFlexTable - Components - Vuero',
})
// this is a local directive (it begins with V..., usable with v-focus)
// that is used to force the focus on input when mounted
const VFocus = {
mounted(el: HTMLInputElement) {
el.focus()
},
}
const selectedRows = ref<number[]>([])
const editCompanyIndex = ref<number>()
const isAllSelected = computed(
() => flexRowsContacts.length === selectedRows.value.length,
)
const columns = {
select: {
label: '',
cellClass: 'is-flex-grow-0',
},
company: {
label: 'Company',
grow: true,
},
type: 'Type',
industry: 'Industry',
status: 'Status',
contacts: {
label: '',
align: 'end',
},
} as const
function toggleSelection() {
if (isAllSelected.value) {
selectedRows.value = []
}
else {
selectedRows.value = flexRowsContacts.map(item => item.id)
}
}
function clickOnRow(row: any) {
if (selectedRows.value.includes(row.id)) {
selectedRows.value = selectedRows.value.filter(i => i !== row.id)
}
else {
selectedRows.value = [...selectedRows.value, row.id]
}
}
function contactUser(row: any) {
alert(`Contacting "${row.company}" ...`)
}
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'VFlexTable',
to: '/components/flextable/',
},
{
label: 'Slots usage',
to: '/components/flextable/slots',
},
]"
/>
<div class="columns">
<div class="column is-12">
<!--Flex Table-->
<VFlexTableWithSlotsDocumentation class="mt-5" />
<div class="mt-4">
<!--VFlexTabe-->
<VFlexTable
:data="flexRowsContacts"
:columns="columns"
compact
rounded
reactive
>
<!-- header-column slot -->
<template #header-column="{ column }">
<VCheckbox
v-if="column.key === 'select'"
class="ml-2 mr-3"
:checked="isAllSelected"
name="all_selected"
color="primary"
square
@click="toggleSelection"
/>
</template>
<!-- body-cell slot -->
<template #body-cell="{ row, column, index, value }">
<VCheckbox
v-if="column.key === 'select'"
v-model="selectedRows"
:value="row.id"
name="selection"
square
/>
<template v-else-if="column.key === 'company'">
<VControl v-if="editCompanyIndex === index">
<VField>
<input
v-model="row[column.key]"
v-focus
type="text"
class="input is-primary-focus"
@blur="editCompanyIndex = undefined"
@keydown.enter.prevent="editCompanyIndex = undefined"
>
</VField>
</VControl>
<a
v-else
class="is-overlay-desktop is-flex is-pushed-mobile is-align-items-center mx-3-desktop edit-icon-link is-clickable"
tabindex="0"
role="button"
@keydown.enter.prevent="editCompanyIndex = index"
@click="editCompanyIndex = index"
>
{{ value }}
<VIcon
class="is-inline ml-1"
icon="lucide:edit-3"
role="img"
aria-label="edit"
/>
</a>
</template>
<VTag
v-else-if="column.key === 'status'"
rounded
:color="
value === 'Suspended'
? 'orange'
: value === 'New'
? 'info'
: value === 'Active'
? 'primary'
: undefined
"
>
{{ value }}
</VTag>
<VAction
v-else-if="column.key === 'contacts'"
hoverable
@click="contactUser(row)"
>
Contact manager
</VAction>
</template>
</VFlexTable>
</div>
<DocumentationDivider
id="with-reactive"
title="With reactive props"
/>
<div class="mt-4">
<VFlexTable
:data="flexRowsContacts"
:columns="columns"
compact
rounded
reactive
>
<template #header-column="{ column }">
<VCheckbox
v-if="column.key === 'select'"
class="ml-2 mr-3"
:checked="isAllSelected"
name="all_selected"
color="primary"
square
@click="toggleSelection"
/>
</template>
<template #body-cell="{ row, column, value }">
<VCheckbox
v-if="column.key === 'select'"
v-model="selectedRows"
:value="row.id"
name="selection"
square
/>
<VTag
v-else-if="column.key === 'status'"
rounded
:color="
value === 'Suspended'
? 'orange'
: value === 'New'
? 'info'
: value === 'Active'
? 'primary'
: undefined
"
>
{{ value }}
</VTag>
<VAction
v-if="column.key === 'contacts'"
hoverable
@click="contactUser(row)"
>
Contact manager
</VAction>
</template>
</VFlexTable>
</div>
<DocumentationDivider
id="without-reactive"
title="Without reactive props"
/>
<div class="mt-4">
<VFlexTable
:data="flexRowsContacts"
:columns="columns"
compact
rounded
>
<template #header-column="{ column }">
<VCheckbox
v-if="column.key === 'select'"
class="ml-2 mr-3"
:checked="isAllSelected"
name="all_selected"
color="primary"
square
@click="toggleSelection"
/>
</template>
<template #body-cell="{ row, column, value }">
<VCheckbox
v-if="column.key === 'select'"
v-model="selectedRows"
:value="row.id"
name="selection"
square
/>
<VTag
v-else-if="column.key === 'status'"
rounded
:color="
value === 'Suspended'
? 'orange'
: value === 'New'
? 'info'
: value === 'Active'
? 'primary'
: undefined
"
>
{{ value }}
</VTag>
<VAction
v-else-if="column.key === 'contacts'"
hoverable
@click="contactUser(row)"
>
Contact manager
</VAction>
</template>
</VFlexTable>
</div>
<DocumentationDivider
id="with-clickable"
title="With clickable props"
/>
<div class="mt-4">
<VFlexTable
:data="flexRowsContacts"
:columns="columns"
clickable
compact
rounded
@row-click="clickOnRow"
>
<template #header-column="{ column }">
<VCheckbox
v-if="column.key === 'select'"
class="ml-2 mr-3"
:checked="isAllSelected"
name="all_selected"
color="primary"
square
@click="toggleSelection"
/>
</template>
<template #body-cell="{ row, column, value }">
<VCheckbox
v-if="column.key === 'select'"
v-model="selectedRows"
:value="row.id"
name="selection"
square
/>
<VTag
v-else-if="column.key === 'status'"
rounded
:color="
value === 'Suspended'
? 'orange'
: value === 'New'
? 'info'
: value === 'Active'
? 'primary'
: undefined
"
>
{{ value }}
</VTag>
<VAction
v-else-if="column.key === 'contacts'"
@click.stop="contactUser(row)"
>
Contact manager
</VAction>
</template>
</VFlexTable>
</div>
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,560 @@
<script setup lang="ts">
import type { VFlexTableWrapperDataResolver } from '/@src/components/base/VFlexTableWrapper.vue'
const { isMobileScreen } = useScreenSize()
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'Async data (advanced)'
})
useHead({
title: 'Async data (advanced) - VFlexTableWrapper - Vuero',
})
// the total data will be set by the fetchData function
const total = ref(0)
// we don't have to set "searchable" parameter
// this will be handled by the fetchData function
const columns = {
name: {
label: 'Username',
media: true,
grow: true,
sortable: true,
},
location: {
label: 'Location',
sortable: true,
},
position: {
label: 'Positions',
sortable: true,
},
actions: {
label: '',
align: 'end',
},
} as const
// this is an example of useXxx function that we can reuse across components.
// it will return writable computeds that works like ref values
// but the values will be sync with the route query params
function useQueryParam() {
const router = useRouter()
const route = useRoute()
// when the params match those value,
// we don't set their value to the query params
const defaultSearch = ''
const defaultSort = ''
const defaultLimit = 10
const defaultPage = 1
const searchTerm = computed({
get: () => {
let searchTermQuery: string
// read "search" from the query params
if (Array.isArray(route?.query?.search)) {
searchTermQuery = route.query.search?.[0] ?? defaultSearch
}
else {
searchTermQuery = route.query.search ?? defaultSearch
}
return searchTermQuery
},
set(value) {
// update the route query params with our new "search" value.
// we can use router.replace instead of router.push
// to not write state to the browser history
router.push({
query: {
search: value === defaultSearch ? undefined : value,
sort: sort.value === defaultSort ? undefined : sort.value,
limit: limit.value === defaultLimit ? undefined : limit.value,
page: page.value === defaultPage ? undefined : page.value,
},
})
},
})
const sort = computed({
get: () => {
let sortQuery: string
// read "sort" from the query params
if (Array.isArray(route?.query?.sort)) {
sortQuery = route.query.sort?.[0] ?? defaultSort
}
else {
sortQuery = route.query.sort ?? defaultSort
}
return sortQuery
},
set(value) {
// update the route query params with our new "sort" value.
// we can use router.replace instead of router.push
// to not write state to the browser history
router.push({
query: {
search: searchTerm.value === defaultSearch ? undefined : searchTerm.value,
sort: value === defaultSort ? undefined : value,
limit: limit.value === defaultLimit ? undefined : limit.value,
page: page.value === defaultPage ? undefined : page.value,
},
})
},
})
const limit = computed({
get: () => {
let limitQuery: number
// read "limit" from the query params
if (Array.isArray(route?.query?.limit)) {
limitQuery = parseInt(route.query.limit[0] ?? `${defaultLimit}`)
}
else {
limitQuery = parseInt(route.query.limit ?? `${defaultLimit}`)
}
if (Object.is(limitQuery, NaN)) {
limitQuery = defaultLimit
}
return limitQuery
},
set(value) {
// update the route query params with our new "limit" value.
// we can use router.replace instead of router.push
// to not write state to the browser history
router.push({
query: {
search: searchTerm.value === defaultSearch ? undefined : searchTerm.value,
sort: sort.value === defaultSort ? undefined : sort.value,
limit: value === defaultLimit ? undefined : value,
page: page.value === defaultPage ? undefined : page.value,
},
})
},
})
const page = computed({
get: () => {
let pageQuery: number
if (Array.isArray(route?.query?.page)) {
pageQuery = parseInt(route.query.page[0] ?? `${defaultPage}`)
}
else {
pageQuery = parseInt(route.query.page ?? `${defaultPage}`)
}
// read "page" from the query params
if (Object.is(pageQuery, NaN)) {
pageQuery = defaultPage
}
return pageQuery
},
set(value) {
// update the route query params with our new "page" value.
// we can use router.replace instead of router.push
// to not write state to the browser history
router.push({
query: {
search: searchTerm.value === defaultSearch ? undefined : searchTerm.value,
sort: sort.value === defaultSort ? undefined : sort.value,
limit: limit.value === defaultLimit ? undefined : limit.value,
page: value === defaultPage ? undefined : value,
},
})
},
})
return reactive({
searchTerm,
sort,
limit,
page,
})
}
const queryParam = useQueryParam()
// the fetchData function will be called each time one of the parameter changes
const $fetch = useApiFetch()
const fetchData: VFlexTableWrapperDataResolver = async ({
searchTerm,
start,
limit,
sort,
controller,
}) => {
// sort will be a string like "name:asc"
let [sortField, sortOrder]
= sort && sort.includes(':') ? sort.split(':') : [undefined, undefined]
// async fetch data to our server
const { _data: users, headers } = await $fetch.raw(`/api/users`, {
query: {
// searchTerm will contains the value of the wrapperState.searchInput
// the update will be debounced to avoid to much requests
q: searchTerm,
_start: start,
_limit: limit,
_sort: sortField,
_order: sortOrder,
},
// controller is an instance of AbortController,
// this allow to abort the request when the state
// is invalidated (before fetchData will be retriggered)
signal: controller?.signal,
})
// wait more time
await sleep(1000)
// our backend send us the count in the headers,
// but we can also get it from another request
if (headers.has('X-Total-Count')) {
total.value = parseInt(headers.get('X-Total-Count') ?? '0')
}
// the return of the function must be an array
return users
}
// those data are for the interaction example
const openedRowId = ref<number>()
function onRowClick(row: any) {
if (openedRowId.value === row.id) {
openedRowId.value = undefined
}
else {
openedRowId.value = row.id
}
}
const incomingCallerId = ref<number>()
function onCallClick(row: any) {
if (incomingCallerId.value === row.id) {
incomingCallerId.value = undefined
}
else {
incomingCallerId.value = row.id
}
}
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'VFlexTable',
to: '/components/flextable/',
},
{
label: 'VFlexTableWrapper',
to: '/components/flextable/wrapper/',
},
{
label: 'Async data (advanced)',
to: '/components/flextable/wrapper/async',
},
]"
/>
<div class="columns">
<div class="column is-12">
<!--Flex Table Advanced-->
<VFlexTableWrapperAdvancedDocumentation />
<!--
We use v-model to let VFlexTableWrapper update queryParam
-->
<VFlexTableWrapper
v-model:page="queryParam.page"
v-model:limit="queryParam.limit"
v-model:searchTerm="queryParam.searchTerm"
v-model:sort="queryParam.sort"
:columns="columns"
:data="fetchData"
:total="total"
class="mt-4"
>
<!--
Here we retrieve the internal wrapperState.
Note that we can not destructure it
-->
<template #default="wrapperState">
<!--Table Pagination-->
<VFlexPagination
v-if="!isMobileScreen"
v-model:current-page="wrapperState.page"
:item-per-page="wrapperState.limit ?? 0"
:total-items="wrapperState.total ?? 0"
:max-links-displayed="2"
no-router
>
<!-- The controls can be updated anywhere in the slot -->
<template #before-pagination>
<VFlex class="mr-4">
<VField>
<VControl icon="lucide:search">
<input
v-model="wrapperState.searchInput"
type="text"
class="input is-rounded"
placeholder="Filter..."
>
</VControl>
</VField>
</VFlex>
</template>
<template #before-navigation>
<VFlex
class="mr-4"
column-gap="1rem"
>
<VButton
:loading="wrapperState.loading"
size="medium"
rounded
@click="wrapperState.fetchData"
>
Refresh
</VButton>
<VField>
<VControl>
<div class="select is-rounded">
<select v-model="wrapperState.limit">
<option :value="1">
1 results per page
</option>
<option :value="10">
10 results per page
</option>
<option :value="15">
15 results per page
</option>
<option :value="25">
25 results per page
</option>
<option :value="50">
50 results per page
</option>
</select>
</div>
</VControl>
</VField>
</VFlex>
</template>
</VFlexPagination>
<VFlexTable
rounded
clickable
:no-header="!wrapperState.loading && wrapperState.data?.length === 0"
@row-click="onRowClick"
>
<template #body>
<!--
The wrapperState.loading will be update
when the fetchData function is running
-->
<div
v-if="wrapperState.loading"
class="flex-list-inner"
>
<div
v-for="key in wrapperState.limit"
:key="key"
class="flex-table-item"
>
<VFlexTableCell :column="{ grow: true, media: true }">
<VPlaceloadAvatar size="medium" />
<VPlaceloadText
:lines="2"
width="60%"
last-line-width="20%"
class="mx-2"
/>
</VFlexTableCell>
<VFlexTableCell>
<VPlaceload
width="60%"
class="mx-1"
/>
</VFlexTableCell>
<VFlexTableCell>
<VPlaceload
width="60%"
class="mx-1"
/>
</VFlexTableCell>
<VFlexTableCell :column="{ align: 'end' }">
<VPlaceload
width="45%"
class="mx-1"
/>
</VFlexTableCell>
</div>
</div>
<!-- This is the empty state -->
<div
v-else-if="wrapperState.data?.length === 0"
class="flex-list-inner"
>
<VPlaceholderSection
title="No matches"
subtitle="There is no data that match your query."
class="my-6"
>
<template #image>
<img
class="light-image"
src="/images/illustrations/placeholders/search-4.svg"
alt=""
>
<img
class="dark-image"
src="/images/illustrations/placeholders/search-4-dark.svg"
alt=""
>
</template>
</VPlaceholderSection>
</div>
</template>
<!-- We can inject content before any rows -->
<template #body-row-pre="{ row }">
<template v-if="row.id === incomingCallerId">
<VProgress
size="tiny"
class="m-0 mb-1"
/>
</template>
</template>
<!-- This is the body cell slot -->
<template #body-cell="{ row, column }">
<template v-if="column.key === 'name'">
<VAvatar
size="medium"
:picture="row.pic"
:badge="row.badge"
:initials="row.initials"
/>
<div>
<span class="dark-text">{{ row.name }}</span>
<VTextEllipsis
width="280px"
mobile-width="180px"
class="light-text"
>
{{ row.bio }}
</VTextEllipsis>
</div>
</template>
<template v-if="column.key === 'actions'">
<VAction>
{{ row.id === openedRowId ? 'Hide details' : 'View details' }}
</VAction>
</template>
</template>
<!-- We can also inject content after rows -->
<template #body-row-post="{ row }">
<template v-if="row.id === incomingCallerId">
<VTags class="mt-2 mb-0">
<VTag
color="primary"
outlined
>
<VIcon
class="is-inline mr-2"
icon="lucide:send"
/>
Calling...
</VTag>
</VTags>
</template>
<template v-if="row.id === openedRowId">
<div class="is-block p-4 my-2 is-rounded">
<div class="dark-text mb-4 is-size-4">
{{ row.name }}'s details
</div>
<VFlex justify-content="space-between">
<VFlexItem>
<VCard>
<pre><code>{{ row }}</code></pre>
</VCard>
</VFlexItem>
<VFlexItem align-self="flex-end">
<VFlex flex-direction="column">
<VButton
v-if="row.id === incomingCallerId"
class="mb-2"
color="danger"
@click="() => onCallClick(row)"
>
<VIcon
class="is-inline mr-2"
icon="lucide:phone-off"
/>
Cancel call
</VButton>
<VButton
color="primary"
outlined
:disabled="row.id === incomingCallerId"
:loading="row.id === incomingCallerId"
@click="() => onCallClick(row)"
>
<VIcon
class="is-inline mr-2"
icon="lucide:phone"
/>
Call {{ row.name }}
</VButton>
</VFlex>
</VFlexItem>
</VFlex>
</div>
</template>
</template>
</VFlexTable>
<!--Table Pagination-->
<VFlexPagination
v-model:current-page="wrapperState.page"
class="mt-5"
:item-per-page="wrapperState.limit ?? 0"
:total-items="wrapperState.total ?? 0"
:max-links-displayed="2"
no-router
/>
</template>
</VFlexTableWrapper>
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,219 @@
<script setup lang="ts">
import type {
VFlexTableWrapperSortFunction,
VFlexTableWrapperFilterFunction,
} from '/@src/components/base/VFlexTableWrapper.vue'
import { users } from '/@src/data/layouts/card-grid-v1'
import { VFlexTableWrapperMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VFlexTableWrapper'
})
useHead({
title: 'VFlexTableWrapper - VFlexTable - Vuero',
})
type User = (typeof users)[0]
// duplicate user data to grow data array
const data: User[] = []
for (let i = 0; i < 1000; i++) {
data.push(...users)
}
// this is a sample for custom sort function
const locationSorter: VFlexTableWrapperSortFunction<User> = ({ order, a, b }) => {
if (order === 'asc') {
return a.location.localeCompare(b.location)
}
else if (order === 'desc') {
return b.location.localeCompare(a.location)
}
return 0
}
// this is a sample for custom filter function
const userFilter: VFlexTableWrapperFilterFunction<User> = ({ searchTerm, row }) => {
if (!searchTerm) {
return true
}
// search either in the name or the bio
return (
row.name.toLocaleLowerCase().includes(searchTerm.toLocaleLowerCase())
|| row.bio.toLocaleLowerCase().includes(searchTerm.toLocaleLowerCase())
)
}
const columns = {
name: {
label: 'Username',
media: true,
grow: true,
searchable: true,
sortable: true,
filter: userFilter,
},
location: {
label: 'Location',
sortable: true,
searchable: true,
sort: locationSorter,
},
role: 'Role',
} as const
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'VFlexTable',
to: '/components/flextable/',
},
{
label: 'VFlexTableWrapper',
to: '/components/flextable/wrapper/',
},
]"
/>
<div class="columns">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<!--Flex Table Wrapper base documentation-->
<VFlexTableWrapperDocumentation />
<VFlexTableWrapper
:columns="columns"
:data="data"
class="mt-4"
>
<!--
Here we retrieve the internal wrapperState.
Note that we can not destructure it
-->
<template #default="wrapperState">
<!-- We can place any content inside the default slot-->
<VFlexTableToolbar>
<template #left>
<!-- We can bind wrapperState.searchInput to any input -->
<VField>
<VControl icon="lucide:search">
<input
v-model="wrapperState.searchInput"
type="text"
class="input is-rounded"
placeholder="Filter..."
>
</VControl>
</VField>
</template>
<template #right>
<!-- We can also bind wrapperState.limit -->
<VField>
<VControl>
<div class="select is-rounded">
<select v-model="wrapperState.limit">
<option :value="1">
1 results per page
</option>
<option :value="10">
10 results per page
</option>
<option :value="15">
15 results per page
</option>
<option :value="25">
25 results per page
</option>
<option :value="50">
50 results per page
</option>
</select>
</div>
</VControl>
</VField>
</template>
</VFlexTableToolbar>
<!--
The VFlexTable "data" and "columns" props
will be inherited from parent VFlexTableWrapper
-->
<VFlexTable rounded>
<!-- Custom "name" cell content -->
<template #body-cell="{ row, column }">
<template v-if="column.key === 'name'">
<VAvatar
size="medium"
:picture="row.medias.avatar"
:badge="row.medias.badge"
:initials="row.initials"
/>
<div>
<span
class="dark-text"
:title="row.name"
>{{ row.shortname }}</span>
<VTextEllipsis
width="240px"
class="light-text"
:title="row.bio"
>
<small>{{ row.bio }}</small>
</VTextEllipsis>
</div>
</template>
</template>
</VFlexTable>
<!-- Table Pagination with wrapperState.page binded-->
<VFlexPagination
v-model:current-page="wrapperState.page"
class="mt-6"
:item-per-page="wrapperState.limit ?? 0"
:total-items="wrapperState.total ?? 0"
:max-links-displayed="5"
no-router
/>
</template>
</VFlexTableWrapper>
<DocumentationMeta
name="VFlexTableWrapper"
:meta="VFlexTableWrapperMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,55 @@
<script setup lang="ts">
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'Input Addons'
})
useHead({
title: 'Input Addons - Forms Elements - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'Forms',
},
{
label: 'Input Addons',
to: '/components/forms/addons',
},
]"
/>
<div class="columns">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
/>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,72 @@
<script setup lang="ts">
import { VControlMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VControl'
})
const breadcrumb = [
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'Forms',
},
{
label: 'VControl',
to: '/components/forms/control',
},
]
useHead({
title: 'VControl - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="breadcrumb"
/>
<div class="columns">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<VControlValidationDocumentation />
<VControlValidationRoundedDocumentation />
<VControlValidationVeeZodDocumentation>
<template #example>
<DocumentationDemoFormValidation />
</template>
</VControlValidationVeeZodDocumentation>
<DocumentationMeta
name="VControl"
:meta="VControlMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,70 @@
<script setup lang="ts">
import { VFieldMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VField'
})
useHead({
title: 'VField - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'Forms',
},
{
label: 'VField',
to: '/components/forms/field',
},
]"
/>
<div class="columns">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<VFieldBaseDocumentation />
<VFieldLabelInlineDocumentation />
<VFieldIdTrackingDocumentation />
<VFieldAddonEndDocumentation />
<VFieldAddonStartDocumentation />
<VFieldAddonRoundedDocumentation />
<VFieldAddonDirectionalDocumentation />
<DocumentationMeta
name="VField"
:meta="VFieldMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,67 @@
<script setup lang="ts">
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'File Input'
})
useHead({
title: 'File Input - Forms Elements - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'Forms',
},
{
label: 'File Input',
to: '/components/forms/file',
},
]"
/>
<div class="columns">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<!--File input button-->
<FileBaseDocumentation />
<!--File input boxed-->
<FileBoxedDocumentation />
<!--File Left-->
<FileLeftDocumentation />
<!--File Right-->
<FileRightDocumentation />
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,92 @@
<script setup lang="ts">
import { VInputMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VInput'
})
useHead({
title: 'VInput - Forms Elements - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'Forms',
},
{
label: 'VInput',
to: '/components/forms/inputs',
},
]"
/>
<div class="columns">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<!--Input-->
<InputBasicDocumentation />
<!--Help Text-->
<InputHelpDocumentation />
<!--Rounded Input-->
<InputRoundedDocumentation />
<!--Focus Colors-->
<InputFocusDocumentation />
<!--Font Awesome-->
<InputFaDocumentation />
<!--Line Icons-->
<InputLineDocumentation />
<!--Lucide Icons-->
<InputFeatherDocumentation />
<!--Loading State-->
<InputLoadingDocumentation />
<!--Disabled Input-->
<InputDisabledDocumentation />
<InputDatasetDocumentation />
<DocumentationMeta
name="VInput"
:meta="VInputMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,75 @@
<script setup lang="ts">
import { VRangeRatingMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'Rating'
})
useHead({
title: 'VRangeRating - Forms Elements - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'Forms',
},
{
label: 'VRangeRating',
to: '/components/forms/range-rating',
},
]"
/>
<div class="columns">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<!--Rating button-->
<RangeRatingBasicDocumentation />
<RangeRatingMaxDocumentation />
<RangeRatingLabelDocumentation />
<RangeRatingReadonlyDocumentation />
<RangeRatingSizesDocumentation />
<RangeRatingCustomIconDocumentation />
<DocumentationMeta
name="VRangeRating"
:meta="VRangeRatingMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,83 @@
<script setup lang="ts">
import { VSelectMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VSelect'
})
useHead({
title: 'VSelect - Forms Elements - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'Forms',
},
{
label: 'VSelect',
to: '/components/forms/selects',
},
]"
/>
<div class="columns">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<!--Select-->
<SelectBaseDocumentation />
<!--Rounded Select-->
<SelectRoundedDocumentation />
<!--Font Awesome-->
<SelectFaDocumentation />
<!--Line Icons-->
<SelectLineDocumentation />
<!--Lucide Icons-->
<SelectFeatherDocumentation />
<!--Loading-->
<SelectLoadingDocumentation />
<!--Multiple-->
<SelectMultipleDocumentation />
<DocumentationMeta
name="VSelect"
:meta="VSelectMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,77 @@
<script setup lang="ts">
import { VTextareaMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VTextarea'
})
useHead({
title: 'VTextarea - Forms Elements - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'Forms',
},
{
label: 'VTextarea',
to: '/components/forms/textarea',
},
]"
/>
<div class="columns">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<!--Textarea-->
<TextareaBaseDocumentation />
<!--Textarea Focus-->
<TextareaFocusDocumentation />
<!--Textarea loading-->
<TextareaLoadingDocumentation />
<!--Textarea disabled-->
<TextareaDisabledDocumentation />
<!--Textarea Addon-->
<TextareaAddonDocumentation />
<DocumentationMeta
name="VTextarea"
:meta="VTextareaMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,67 @@
<script setup lang="ts">
import { VGridMeta, VGridItemMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VGrid'
})
useHead({
title: 'VGrid - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'VGrid',
to: '/components/flex',
},
]"
/>
<div class="columns">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<!--VGrid-->
<VGridBaseDocumentation />
<DocumentationMeta
name="VGrid"
:meta="VGridMeta"
/>
<DocumentationMeta
name="VGridItem"
:meta="VGridItemMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,350 @@
<script setup lang="ts">
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'Helpers'
})
useHead({
title: 'Helpers - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'Helpers',
to: '/components/helpers',
},
]"
/>
<div class="columns is-multiline">
<div class="column is-12">
<!--Theme Colors-->
<HelpersDocumentation />
<div class="columns is-multiline">
<div class="column is-12">
<div class="s-card demo-table">
<table
class="table is-hoverable is-fullwidth helper-table"
aria-label="Vuero CSS classes helpers"
>
<thead>
<tr>
<td />
<th scope="col">
Helper
</th>
<th scope="col">
Description
</th>
</tr>
</thead>
<tbody>
<tr>
<th
scope="rowgroup"
rowspan="4"
class="category"
>
<strong>Spacing</strong>
</th>
<td class="name">
Margin (<code>m-*-*</code>)
</td>
<td class="description">
Sets a margin on the target element following the
<code>m-*-*</code> pattern, where the first <code>*</code> stands
for directon (<code>t</code>, <code>b</code>, <code>l</code> and
<code>r</code>) and the second one for pixel amount. Minimum and
maximum pixel amounts are <code>5</code> and <code>100</code>.
</td>
</tr>
<tr>
<td class="name">
Padding (<code>p-*-*</code>)
</td>
<td class="description">
Sets a padding on the target element following the
<code>p-*-*</code> pattern, where the first <code>*</code> stands
for directon (<code>t</code>, <code>b</code>, <code>l</code> and
<code>r</code>) and the second one for pixel amount. Minimum and
maximum pixel amounts are <code>5</code> and <code>100</code>.
</td>
</tr>
<tr>
<td class="name">
No Margin (<code>no-margin-*</code>)
</td>
<td class="description">
Sets a margin on the target element following the
<code>no-margin-*</code> pattern, where the first
<code>*</code> stands for directon (<code>all</code>,
<code>top</code>, <code>bottom</code>, <code>left</code> and
<code>right</code>).
</td>
</tr>
<tr>
<td class="name">
No Padding (<code>no-padding-*</code>)
</td>
<td class="description">
Sets a padding on the target element following the
<code>no-padding-*</code> pattern, where the first
<code>*</code> stands for directon (<code>all</code>,
<code>top</code>, <code>bottom</code>, <code>left</code> and
<code>right</code>).
</td>
</tr>
<tr>
<th
scope="rowgroup"
rowspan="3"
class="category"
>
<strong>States</strong>
</th>
<td class="name">
Hidden (<code>is-hidden</code>)
</td>
<td class="description">
Sets the <code>display</code> property to <code>none</code> on the
target element.
</td>
</tr>
<tr>
<td class="name">
VHidden (<code>is-vhidden</code>)
</td>
<td class="description">
Sets the <code>visibility</code> property to <code>hidden</code> on
the target element.
</td>
</tr>
<tr>
<td class="name">
Disabled (<code>is-disabled</code>)
</td>
<td class="description">
Sets the <code>pointer-events</code> property to
<code>none</code> on the target element and decreases its opacity.
</td>
</tr>
<tr>
<th
scope="row"
class="category"
>
<strong>Scroll</strong>
</th>
<td class="name">
Slimscroll (<code>has-slimscroll</code>)
</td>
<td class="description">
Sets the <code>overflowY</code> property to <code>auto</code> on the
target element and tweaks the webkit scrollbar.
</td>
</tr>
<tr>
<th
scope="row"
class="category"
>
<strong>Scroll</strong>
</th>
<td class="name">
Slimscroll X (<code>has-slimscroll-x</code>)
</td>
<td class="description">
Sets the <code>overflowX</code> property to <code>auto</code> on the
target element and tweaks the webkit scrollbar.
</td>
</tr>
<tr>
<th
scope="row"
class="category"
>
<strong>Scroll</strong>
</th>
<td class="name">
Slimscroll X and Y (<code>has-slimscroll-all</code>)
</td>
<td class="description">
Sets the <code>overflow</code> property to <code>auto</code> on the
target element and tweaks the webkit scrollbar.
</td>
</tr>
<tr>
<th
scope="rowgroup"
rowspan="2"
class="category"
>
<strong>Dark Mode</strong>
</th>
<td class="name">
Images
</td>
<td class="description">
Toggle images between light mode and dark mode using the
corresponding classes:
<code>light-image</code> and <code>dark-image</code>. Displayed
images will be displayed as <code>inline-block</code>. To display
images as <code>block</code> use the
<code>light-image-block</code> and
<code>dark-image-block</code> classes instead.
</td>
</tr>
<tr>
<td class="name">
Dark Background (<code>is-dark-bg-*</code>)
</td>
<td class="description">
Sets the <code>background-color</code> property to the specidifed
value, where <code>*</code> is a percentage of the dark mode main
color. Accepted values: 2, 3, 4, 6.
</td>
</tr>
<tr>
<th
scope="rowgroup"
rowspan="4"
class="category"
>
<strong>Responsive</strong>
</th>
<td class="name">
Hidden mobile (<code>h-hidden-mobile</code>)
</td>
<td class="description">
Sets the <code>display</code> property to <code>none</code> on the
target element when on a mobile display.
</td>
</tr>
<tr>
<td class="name">
Hidden tablet (<code>h-hidden-tablet-p</code>)
</td>
<td class="description">
Sets the <code>display</code> property to <code>none</code> on the
target element when on a tablet display in portrait mode.
</td>
</tr>
<tr>
<td class="name">
Hidden tablet (<code>h-hidden-tablet-l</code>)
</td>
<td class="description">
Sets the <code>display</code> property to <code>none</code> on the
target element when on a tablet display in landscape mode.
</td>
</tr>
<tr>
<td class="name">
Hidden desktop (<code>h-hidden-desktop</code>)
</td>
<td class="description">
Sets the <code>display</code> property to <code>none</code> on the
target element when on a desktop display.
</td>
</tr>
<tr>
<th
scope="rowgroup"
rowspan="4"
class="category"
>
<strong>Typography</strong>
</th>
<td class="name">
Font (<code>.is-font</code>)
</td>
<td class="description">
Sets the <code>font-family</code> property to the
<code>$font</code> variable value.
</td>
</tr>
<tr>
<td class="name">
Font Alt (<code>.is-font-alt</code>)
</td>
<td class="description">
Sets the <code>font-family</code> property to the
<code>$font-alt</code> variable value.
</td>
</tr>
<tr>
<td class="name">
Font Weight (<code>.is-weight-*</code>)
</td>
<td class="description">
Sets the <code>font-weight</code> property to the specidifed value,
where <code>*</code> is a value between 300 and 900.
</td>
</tr>
<tr>
<td class="name">
Font Size (<code>.rem-*</code>)
</td>
<td class="description">
Sets the <code>font-size</code> property to the specidifed value,
where <code>*</code> is a value between 70 and 100. Increments by 5.
</td>
</tr>
<tr>
<th
scope="rowgroup"
rowspan="2"
class="category"
>
<strong>Stay Focus</strong>
</th>
<td class="name">
Container (<code>.stay-focus-container</code>)
</td>
<td class="description">
Sets the stay focus container. Will blur all child
<code>.stay-focus-element</code> except the hovered one.
</td>
</tr>
<tr>
<td class="name">
Element (<code>.stay-focus-element</code>)
</td>
<td class="description">
Element that will be blured when user hover over the container.
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
.is-dark strong {
color: var(--dark-dark-text);
}
</style>

View File

@@ -0,0 +1,80 @@
<script setup lang="ts">
import { VIconBoxMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VIconBox'
})
useHead({
title: 'VIconBox - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'VIconBox',
to: '/components/icon-box',
},
]"
/>
<div class="columns">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<!--Icon Box-->
<IconBoxDefaultDocumentation />
<!--Box Colors-->
<IconBoxColorsDocumentation />
<!--Box Border-->
<IconBoxSquaredBorderDocumentation />
<!--Box Rounded-->
<IconBoxRoundedDocumentation />
<!--Box Colors-->
<IconBoxRoundedBorderDocumentation />
<!--Lucide Icons-->
<IconBoxFeatherDocumentation />
<!--Font Awesome-->
<IconBoxFaDocumentation />
<DocumentationMeta
name="VIconBox"
:meta="VIconBoxMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,64 @@
<script setup lang="ts">
import { VIconButtonMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VIconButton'
})
useHead({
title: 'VIconButton - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'Buttons',
},
{
label: 'VIconButton',
to: '/components/icon-button',
},
]"
/>
<div class="columns">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<VIconButtonDefaultDocumentation />
<DocumentationMeta
name="VIconButton"
:meta="VIconButtonMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,67 @@
<script setup lang="ts">
import { VIconWrapMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VIconWrap'
})
useHead({
title: 'VIconWrap - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'VIconWrap',
to: '/components/icon-wrap',
},
]"
/>
<div class="columns">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<!--IconBox-->
<IconWrapDefaultDocumentation />
<IconWrapColorsDocumentation />
<IconWrapSizesDocumentation />
<IconWrapDarkDocumentation />
<IconWrapTooltipsDocumentation />
<DocumentationMeta
name="VIconWrap"
:meta="VIconWrapMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,126 @@
<script setup lang="ts">
import { fontAwesome } from '/@src/data/icons/fontAwesome'
const { text, copy, copied } = useClipboard()
const { y } = useWindowScroll()
const filter = ref('')
const isScrolling = computed(() => {
return y.value > 30
})
const filteredIcons = computed(() => {
if (filter.value === '') {
return fontAwesome
}
return fontAwesome.filter((icon) => {
return icon.name.match(new RegExp(filter.value, 'i'))
})
})
function getSnippet(icon: any) {
return `<i class="fas fa-${icon.name}" aria-hidden="true"></i>`
}
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'Font Awesome Icons'
})
useHead({
title: 'Font Awesome Icons - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'Icons',
},
{
label: 'Font Awesome',
to: '/components/icons/font-awesome',
},
]"
/>
<div class="columns">
<div class="column is-12">
<!--Font Awesome-->
<IconsFaDocumentation />
<DocumentationDemoCard>
<div
class="card-inner"
:class="{ 'is-scrolling': isScrolling }"
>
<VFlex
justify-content="flex-end"
class="demo-icon-search py-4 px-6"
>
<VField>
<VControl icon="lucide:search">
<input
v-model="filter"
type="search"
class="input is-rounded"
placeholder="Search font awesome icons ..."
>
</VControl>
</VField>
</VFlex>
<ul class="demo-icon-list">
<li
v-for="icon in filteredIcons"
:id="icon.name"
:key="icon.name"
role="button"
class="icon w-grid-2 textFilter-target w-grid-4-l w-grid-6-xl w-grid-8-2x pr4 pb2 pt2 bb bw1 b--gray1 hover-black bw0-pr db fl-pr is-copy-trigger"
tabindex="0"
@keydown.enter.prevent="copy(getSnippet(icon))"
@click="copy(getSnippet(icon))"
>
<dl class="dt clpse w-100 ma0 pa0">
<dt class="dtc v-top tl w2">
<span class="fa-fw select-all fas">{{ icon.char }}</span>
</dt>
<dd
class="ma0 pa0 pr2 select-all word-wrap dtc v-top tl f2 icon-name textFilter-match"
>
{{ icon.name }}
</dd>
<dd class="ma0 pa0 select-all gray5 dtc v-top tr f2 icon-unicode">
{{ icon.charCode }}
</dd>
</dl>
<Transition name="fade-fast">
<span
v-if="copied && text === getSnippet(icon)"
class="is-copied"
>
copied!
</span>
</Transition>
</li>
</ul>
</div>
</DocumentationDemoCard>
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,118 @@
<script setup lang="ts">
import { iconifyFeather } from '/@src/data/icons/iconifyFeather'
const { text, copy, copied } = useClipboard()
const { y } = useWindowScroll()
const filter = ref('')
const isScrolling = computed(() => {
return y.value > 30
})
const filteredIcons = computed(() => {
if (filter.value === '') {
return iconifyFeather
}
return iconifyFeather.filter((icon) => {
return icon.name.match(new RegExp(filter.value, 'i'))
})
})
function getSnippet(icon: any) {
return `<VIcon icon="lucide:${icon.name}" />`
}
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'Iconify Icons'
})
useHead({
title: 'Iconify Icons - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'Icons',
},
{
label: 'Iconify',
to: '/components/icons/iconify',
},
]"
/>
<div class="columns">
<div class="column is-12">
<!--Iconify Icons-->
<IconsIconifyDocumentation />
<DocumentationDemoCard>
<div
class="card-inner"
:class="{ 'is-scrolling': isScrolling }"
>
<VFlex
justify-content="flex-end"
class="demo-icon-search py-4 px-6"
>
<VField>
<VControl icon="lucide:search">
<input
v-model="filter"
type="search"
class="input is-rounded"
placeholder="Search Lucide Icons ..."
>
</VControl>
</VField>
</VFlex>
<ul class="demo-icon-list">
<li
v-for="icon in filteredIcons"
:key="icon.name"
class="textFilter-target is-copy-trigger"
tabindex="0"
role="button"
@keydown.enter.prevent="copy(getSnippet(icon))"
@click="copy(getSnippet(icon))"
>
<VIcon
:icon="icon.dataicon"
/>
<p class="textFilter-match">
{{ icon.name }}
</p>
<Transition name="fade-fast">
<span
v-if="copied && text === getSnippet(icon)"
class="is-copied"
>
copied!
</span>
</Transition>
</li>
</ul>
</div>
</DocumentationDemoCard>
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,128 @@
<script setup lang="ts">
import { lineIcons } from '/@src/data/icons/lineIcons'
const { text, copy, copied } = useClipboard()
const { y } = useWindowScroll()
const filter = ref('')
const isScrolling = computed(() => {
return y.value > 30
})
const filteredIcons = computed(() => {
if (filter.value === '') {
return lineIcons
}
const filterRe = new RegExp(filter.value, 'i')
return lineIcons.filter((icon) => {
return icon.className.match(filterRe)
})
})
function getSnippet(icon: any) {
return ` <i class="lnil ${icon.className}" aria-hidden="true"></i>`
}
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'Line Icons Icons'
})
useHead({
title: 'Line Icons Icons - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'Icons',
},
{
label: 'Line Icons',
to: '/components/icons/line-icons-light',
},
]"
/>
<div class="columns">
<div class="column is-12">
<!--Line Icons-->
<IconsLineLightDocumentation />
<DocumentationDemoCard class="mt-4">
<div
class="card-inner"
:class="{ 'is-scrolling': isScrolling }"
>
<VFlex
justify-content="flex-end"
class="demo-icon-search py-4 px-6"
>
<VField>
<VControl icon="lucide:search">
<input
v-model="filter"
type="search"
class="input is-rounded"
placeholder="Search line icons ..."
>
</VControl>
</VField>
</VFlex>
<ul class="demo-icon-list">
<li
v-for="icon in filteredIcons"
:key="icon.className"
role="button"
class="textFilter-target is-copy-trigger"
tabindex="0"
@keydown.enter.prevent="copy(getSnippet(icon))"
@click="copy(getSnippet(icon))"
>
<i
aria-hidden="true"
class="lnil"
:class="icon.className"
/>
<p class="textFilter-match">
{{ icon.className }}
</p>
<em>{{ icon.className }}</em>
<input
type="text"
maxlength="1"
readonly
:value="icon.char"
>
<Transition name="fade-fast">
<span
v-if="copied && text === getSnippet(icon)"
class="is-copied"
>
copied!
</span>
</Transition>
</li>
</ul>
</div>
</DocumentationDemoCard>
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,129 @@
<script setup lang="ts">
import { lineIconsRegular } from '/@src/data/icons/lineIconsRegular'
const { text, copy, copied } = useClipboard()
const { y } = useWindowScroll()
const filter = ref('')
const isScrolling = computed(() => {
return y.value > 30
})
const filteredIcons = computed(() => {
if (filter.value === '') {
return lineIconsRegular
}
const filterRe = new RegExp(filter.value, 'i')
return lineIconsRegular.filter((icon) => {
return icon.className.match(filterRe)
})
})
function getSnippet(icon: any) {
return ` <i class="lnir ${icon.className}" aria-hidden="true"></i>`
}
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'Line Icons Regular Icons'
})
useHead({
title: 'Line Icons Regular Icons - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'Icons',
},
{
label: 'Line Icons Regular',
to: '/components/icons/line-icons-regular',
},
]"
/>
<div class="columns">
<div class="column is-12">
<!--Line Icons-->
<IconsLineRegularDocumentation />
<DocumentationDemoCard class="mt-4">
<div
class="card-inner"
:class="{ 'is-scrolling': isScrolling }"
>
<VFlex
justify-content="flex-end"
class="demo-icon-search py-4 px-6"
>
<VField>
<VControl icon="lucide:search">
<input
v-model="filter"
type="search"
class="input is-rounded"
placeholder="Search regular line icons ..."
>
</VControl>
</VField>
</VFlex>
<ul class="demo-icon-list">
<li
v-for="icon in filteredIcons"
:key="icon.className"
class="textFilter-target is-copy-trigger"
tabindex="0"
role="button"
@keydown.enter.prevent="copy(getSnippet(icon))"
@click="copy(getSnippet(icon))"
>
<i
aria-hidden="true"
class="lnir"
:class="icon.className"
/>
<p class="textFilter-match">
{{ icon.className }}
</p>
<em>{{ icon.className }}</em>
<input
type="text"
maxlength="1"
readonly
:value="icon.char"
>
<Transition name="fade-fast">
<span
v-if="copied && text === getSnippet(icon)"
class="is-copied"
>
copied!
</span>
</Transition>
</li>
</ul>
</div>
</DocumentationDemoCard>
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,628 @@
<script setup lang="ts">
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'Components Hub'
})
useHead({
title: 'Components - Vuero',
})
</script>
<template>
<main class="card-hub">
<div class="columns">
<div class="column is-12">
<div class="hub-wrapper">
<div class="hub-header has-text-centered">
<VAvatar
size="xl"
picture="/images/avatars/svg/vuero-2.svg"
badge="https://media.cssninja.io/content/photos/misc/buoy.jpg"
/>
<h3 class="title is-4 is-narrow is-thin">
Vuero Components
</h3>
<p class="light-text">
This is the Vuero components library with detailed code examples.
</p>
</div>
<div class="hub-body">
<div class="body-inner">
<!--Component/buttons-->
<HubCard
title="Buttons"
content="Get familiar with the Vuero button styles. Code examples included."
>
<RouterLink to="/components/button">
Get Started
</RouterLink>
<template #icon>
<img
class="light-image"
src="/images/illustrations/components/buttons.svg"
alt=""
>
<img
class="dark-image"
src="/images/illustrations/components/buttons-dark.svg"
alt=""
>
</template>
</HubCard>
<!--Component/icons-->
<HubCard
title="Icons"
content="Get familiar with the Vuero icon libraries. Code examples included."
>
<RouterLink to="/components/icons/line-icons-light">
Get Started
</RouterLink>
<template #icon>
<img
class="light-image"
src="/images/illustrations/components/icons.svg"
alt=""
>
<img
class="dark-image"
src="/images/illustrations/components/icons-dark.svg"
alt=""
>
</template>
</HubCard>
<!--Component/dropdowns-->
<HubCard
title="Dropdowns"
content="Get familiar with the Vuero dropdown styles. Code examples included."
>
<RouterLink to="/components/dropdown">
Get Started
</RouterLink>
<template #icon>
<img
class="light-image"
src="/images/illustrations/components/dropdown.svg"
alt=""
>
<img
class="dark-image"
src="/images/illustrations/components/dropdown-dark.svg"
alt=""
>
</template>
</HubCard>
<!--Component/modals-->
<HubCard
title="Modals"
content="Get familiar with the Vuero modal styles. Code examples included."
>
<RouterLink to="/components/modal">
Get Started
</RouterLink>
<template #icon>
<img
class="light-image"
src="/images/illustrations/components/modal.svg"
alt=""
>
<img
class="dark-image"
src="/images/illustrations/components/modal-dark.svg"
alt=""
>
</template>
</HubCard>
<!--Component/progress-->
<HubCard
title="Progress"
content="Get familiar with the Vuero progress bar styles. Code examples included."
>
<RouterLink to="/components/progress">
Get Started
</RouterLink>
<template #icon>
<img
class="light-image"
src="/images/illustrations/components/progress.svg"
alt=""
>
<img
class="dark-image"
src="/images/illustrations/components/progress-dark.svg"
alt=""
>
</template>
</HubCard>
<!--Component/forms-->
<HubCard
title="Forms"
content="Get familiar with the Vuero form elements. Code examples included."
>
<RouterLink to="/components/forms/inputs">
Get Started
</RouterLink>
<template #icon>
<img
class="light-image"
src="/images/illustrations/components/forms.svg"
alt=""
>
<img
class="dark-image"
src="/images/illustrations/components/forms-dark.svg"
alt=""
>
</template>
</HubCard>
<!--Component/checkbox-->
<HubCard
title="Checkboxes"
content="Get familiar with the Vuero checkboxes styles. Code examples included."
>
<RouterLink to="/components/checkbox">
Get Started
</RouterLink>
<template #icon>
<img
class="light-image"
src="/images/illustrations/components/checkboxes.svg"
alt=""
>
<img
class="dark-image"
src="/images/illustrations/components/checkboxes-dark.svg"
alt=""
>
</template>
</HubCard>
<!--Component/switch-block-->
<HubCard
title="Switches"
content="Get familiar with the Vuero switches styles. Code examples included."
>
<RouterLink to="/components/switch-block">
Get Started
</RouterLink>
<template #icon>
<img
class="light-image"
src="/images/illustrations/components/switches.svg"
alt=""
>
<img
class="dark-image"
src="/images/illustrations/components/switches-dark.svg"
alt=""
>
</template>
</HubCard>
<!--Component/content-->
<HubCard
title="Content"
content="Get familiar with the Vuero content styles. Code examples included."
>
<RouterLink to="/components/content">
Get Started
</RouterLink>
<template #icon>
<img
class="light-image"
src="/images/illustrations/components/lists.svg"
alt=""
>
<img
class="dark-image"
src="/images/illustrations/components/lists-dark.svg"
alt=""
>
</template>
</HubCard>
<!--Component/tags-->
<HubCard
title="Tags"
content="Get familiar with the Vuero tags styles. Code examples included."
>
<RouterLink to="/components/tag">
Get Started
</RouterLink>
<template #icon>
<img
class="light-image"
src="/images/illustrations/components/tags.svg"
alt=""
>
<img
class="dark-image"
src="/images/illustrations/components/tags-dark.svg"
alt=""
>
</template>
</HubCard>
<!--Component/tabs-->
<HubCard
title="Tabs"
content="Get familiar with the Vuero nav tabs styles. Code examples included."
>
<RouterLink to="/components/tabs">
Get Started
</RouterLink>
<template #icon>
<img
class="light-image"
src="/images/illustrations/components/tabs.svg"
alt=""
>
<img
class="dark-image"
src="/images/illustrations/components/tabs-dark.svg"
alt=""
>
</template>
</HubCard>
<!--Component/cards-->
<HubCard
title="Cards"
content="Get familiar with the Vuero card styles. Code examples included."
>
<RouterLink to="/components/card/">
Get Started
</RouterLink>
<template #icon>
<img
class="light-image"
src="/images/illustrations/components/cards.svg"
alt=""
>
<img
class="dark-image"
src="/images/illustrations/components/cards-dark.svg"
alt=""
>
</template>
</HubCard>
<!--Component/avatars-->
<HubCard
title="Avatars"
content="Get familiar with the Vuero avatar styles. Code examples included."
>
<RouterLink to="/components/avatar/">
Get Started
</RouterLink>
<template #icon>
<img
class="light-image"
src="/images/illustrations/components/avatar.svg"
alt=""
>
<img
class="dark-image"
src="/images/illustrations/components/avatar-dark.svg"
alt=""
>
</template>
</HubCard>
<!--Component/accordions-->
<HubCard
title="Accordions"
content="Get familiar with the Vuero accordion styles. Code examples included."
>
<RouterLink to="/components/accordion/">
Get Started
</RouterLink>
<template #icon>
<img
class="light-image"
src="/images/illustrations/components/accordion.svg"
alt=""
>
<img
class="dark-image"
src="/images/illustrations/components/accordion-dark.svg"
alt=""
>
</template>
</HubCard>
<!--Component/breadcrumbs-->
<HubCard
title="Breadcrumbs"
content="Get familiar with the Vuero breadcrumb styles. Code examples included."
>
<RouterLink to="/components/breadcrumb">
Get Started
</RouterLink>
<template #icon>
<img
class="light-image"
src="/images/illustrations/components/breadcrumb.svg"
alt=""
>
<img
class="dark-image"
src="/images/illustrations/components/breadcrumb-dark.svg"
alt=""
>
</template>
</HubCard>
<!--Component/iconbox-->
<HubCard
title="Icon Box"
content="Get familiar with the Vuero icon boxes styles. Code examples included."
>
<RouterLink to="/components/icon-box">
Get Started
</RouterLink>
<template #icon>
<img
class="light-image"
src="/images/illustrations/components/icon-box.svg"
alt=""
>
<img
class="dark-image"
src="/images/illustrations/components/icon-box-dark.svg"
alt=""
>
</template>
</HubCard>
<!--Component/flextables-->
<HubCard
title="Flex Table"
content="Get familiar with the Vuero flex table styles. Code examples included."
>
<RouterLink to="/components/flextable/">
Get Started
</RouterLink>
<template #icon>
<img
class="light-image"
src="/images/illustrations/components/table.svg"
alt=""
>
<img
class="dark-image"
src="/images/illustrations/components/table-dark.svg"
alt=""
>
</template>
</HubCard>
<!--Component/snacks-->
<HubCard
title="Snacks"
content="Get familiar with the Vuero snacks styles. Code examples included."
>
<RouterLink to="/components/snack">
Get Started
</RouterLink>
<template #icon>
<img
class="light-image"
src="/images/illustrations/components/snacks.svg"
alt=""
>
<img
class="dark-image"
src="/images/illustrations/components/snacks-dark.svg"
alt=""
>
</template>
</HubCard>
</div>
</div>
<div class="hub-footer">
<p>
<a
class="action-link"
tabindex="0"
>Online Documentation</a>
</p>
</div>
</div>
</div>
</div>
</main>
</template>
<style lang="scss">
.card-hub {
max-width: 840px;
margin: 0 auto;
.hub-header {
text-align: center;
> img {
display: block;
margin: 0 auto;
max-width: 300px;
}
.v-avatar {
margin: 0 auto 12px;
}
.anim-icon {
margin-bottom: 12px;
}
p {
font-size: 1.15rem;
}
}
.hub-body {
padding: 10px 0 20px;
.body-inner {
display: flex;
flex-wrap: wrap;
.hub-card {
background-color: var(--white);
border-radius: var(--radius-large);
border: 1px solid color-mix(in oklab, var(--fade-grey), black 3%);
width: calc(50% - 20px);
max-width: calc(50% - 20px);
margin: 10px;
transition: all 0.3s; // transition-all test
&:hover {
box-shadow: var(--light-box-shadow);
.hub-card-body {
img {
filter: grayscale(0);
opacity: 1;
}
}
}
.hub-card-body {
display: flex;
flex-direction: row;
justify-content: space-between;
padding: 24px;
.inner-content {
.title {
font-size: 1.3rem;
margin-bottom: 8px;
}
p {
max-width: 220px;
}
.partner-name {
span {
font-weight: 500;
color: var(--dark-text);
}
}
}
img {
display: block;
width: 100%;
max-width: 100px;
height: 100px;
filter: grayscale(1);
opacity: 0.6;
transition: all 0.3s; // transition-all test
}
}
.hub-card-foot {
border-top: 1px dashed color-mix(in oklab, var(--fade-grey), black 4%);
border-top-width: 1.6px;
padding: 18px 24px;
a {
color: var(--primary);
opacity: 0.6;
font-weight: 500;
transition: opacity 0.3s;
&:hover,
&:focus {
opacity: 1;
}
}
}
}
}
}
.hub-footer {
text-align: center;
padding-top: 20px;
p {
font-weight: 500;
a {
position: relative;
transition: color 0.3s;
}
}
}
}
.is-dark {
.card-hub {
.hub-header {
.v-avatar {
.badge {
border-color: color-mix(in oklab, var(--dark-sidebar), white 6%) !important;
}
}
}
.hub-body {
.body-inner {
.hub-card {
background: color-mix(in oklab, var(--dark-sidebar), white 6%);
border-color: color-mix(in oklab, var(--dark-sidebar), white 12%);
.hub-card-foot {
border-color: color-mix(in oklab, var(--muted-grey), black 25%);
a {
color: var(--primary);
}
}
}
}
}
}
}
@media only screen and (width <= 767px) {
.card-hub {
.hub-body {
.body-inner {
.hub-card {
width: calc(100% - 20px);
max-width: calc(100% - 20px);
}
}
}
}
}
</style>

View File

@@ -0,0 +1,192 @@
<script setup lang="ts">
import { VLoaderMeta } from '/@src/data/documentation/components-meta'
const isLoaderActive = ref(false)
const toggleLoaders = () => {
isLoaderActive.value = !isLoaderActive.value
}
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VLoader'
})
useHead({
title: 'VLoader - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'VLoader',
to: '/components/loader',
},
]"
/>
<div class="columns">
<div class="column is-12">
<!--Loader-->
<VLoaderDocumentation />
<div class="mb-4">
<VButton
color="primary"
elevated
@click="toggleLoaders"
>
<span v-if="!isLoaderActive">Show Loaders</span>
<span v-else>Hide Loaders</span>
</VButton>
</div>
<div class="columns is-multiline">
<div class="column is-4">
<VLoader
size="small"
:active="isLoaderActive"
grey
>
<VCardAction
avatar="https://media.cssninja.io/content/avatars/19.jpg"
badge="/images/icons/flags/germany.svg"
title="Greta K."
subtitle="Sales Manager"
>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quibusnam
praeteritis? At multis se probavit. Quoniam, si dis placet, ab Epicuro
loqui discimus. Et ille ridens.
</p>
</VCardAction>
</VLoader>
</div>
<div class="column is-4">
<VLoader
card="regular"
size="small"
:active="isLoaderActive"
>
<VCardAction
avatar="https://media.cssninja.io/content/avatars/19.jpg"
badge="/images/icons/flags/germany.svg"
title="Greta K."
subtitle="Sales Manager"
>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quibusnam
praeteritis? At multis se probavit. Quoniam, si dis placet, ab Epicuro
loqui discimus. Et ille ridens.
</p>
</VCardAction>
</VLoader>
</div>
<div class="column is-4">
<VLoader
card="regular"
size="small"
:active="isLoaderActive"
translucent
>
<VCardAction
avatar="https://media.cssninja.io/content/avatars/19.jpg"
badge="/images/icons/flags/germany.svg"
title="Greta K."
subtitle="Sales Manager"
>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quibusnam
praeteritis? At multis se probavit. Quoniam, si dis placet, ab Epicuro
loqui discimus. Et ille ridens.
</p>
</VCardAction>
</VLoader>
</div>
<div class="column is-4">
<VLoader
card="smooth"
:active="isLoaderActive"
>
<VCardAction
avatar="https://media.cssninja.io/content/avatars/19.jpg"
badge="/images/icons/flags/germany.svg"
title="Greta K."
subtitle="Sales Manager"
radius="smooth"
>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quibusnam
praeteritis? At multis se probavit. Quoniam, si dis placet, ab Epicuro
loqui discimus. Et ille ridens.
</p>
</VCardAction>
</VLoader>
</div>
<div class="column is-4">
<VLoader
card="rounded"
size="large"
:active="isLoaderActive"
>
<VCardAction
avatar="https://media.cssninja.io/content/avatars/19.jpg"
badge="/images/icons/flags/germany.svg"
title="Greta K."
subtitle="Sales Manager"
radius="rounded"
>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quibusnam
praeteritis? At multis se probavit. Quoniam, si dis placet, ab Epicuro
loqui discimus. Et ille ridens.
</p>
</VCardAction>
</VLoader>
</div>
<div class="column is-4">
<VLoader
card="rounded"
size="xl"
:active="isLoaderActive"
>
<VCardAction
avatar="https://media.cssninja.io/content/avatars/19.jpg"
badge="/images/icons/flags/germany.svg"
title="Greta K."
subtitle="Sales Manager"
radius="rounded"
>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quibusnam
praeteritis? At multis se probavit. Quoniam, si dis placet, ab Epicuro
loqui discimus. Et ille ridens.
</p>
</VCardAction>
</VLoader>
</div>
</div>
<DocumentationMeta
name="VLoader"
:meta="VLoaderMeta"
/>
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,63 @@
<script setup lang="ts">
import { VMarkdownEditorMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VMarkdownEditor'
})
useHead({
title: 'VMarkdownEditor - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'VMarkdownEditor',
to: '/components/markdown-editor',
},
]"
/>
<div class="columns">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<VMarkdownEditorBaseDocumentation />
<VMarkdownEditorToolbarDocumentation />
<VMarkdownEditorCustomPreviewDocumentation />
<DocumentationMeta
name="VMarkdownEditor"
:meta="VMarkdownEditorMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,79 @@
<script setup lang="ts">
import { demoMarkdown } from '/@src/data/documentation/markdown'
import { VMarkdownPreviewMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const { y } = useWindowScroll()
const isScrolling = computed(() => {
return y.value > 30
})
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VMarkdownPreview'
})
useHead({
title: 'VMarkdownPreview - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'VMarkdownPreview',
to: '/components/markdown-preview',
},
]"
/>
<div class="columns">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<VMarkdownPreviewBaseDocumentation />
<DocumentationDemoCard class="mt-4">
<div
class="card-inner"
:class="{ 'is-scrolling': isScrolling }"
>
<VMarkdownPreview
size="medium"
max-width="small"
:source="demoMarkdown"
/>
</div>
</DocumentationDemoCard>
<DocumentationMeta
name="VMarkdownPreview"
:meta="VMarkdownPreviewMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,65 @@
<script setup lang="ts">
import { VMessageMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VMessage'
})
useHead({
title: 'VMessage - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'VMessage',
to: '/components/message',
},
]"
/>
<div class="columns">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<!--Message-->
<VMessageBaseDocumentation />
<!--Message Colors-->
<VMessageColorsDocumentation />
<DocumentationMeta
name="VMessage"
:meta="VMessageMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,914 @@
<script setup lang="ts">
import { VModalMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const leftActionsOpen = ref(false)
const centeredActionsOpen = ref(false)
const rightActionsOpen = ref(false)
const customLabelActionsOpen = ref(false)
const smallOpen = ref(false)
const standardOpen = ref(false)
const mediumOpen = ref(false)
const largeOpen = ref(false)
const bigOpen = ref(false)
const smallFormOpen = ref(false)
const standardFormOpen = ref(false)
const mediumFormOpen = ref(false)
const largeFormOpen = ref(false)
const bigFormOpen = ref(false)
const noscrollOpen = ref(false)
const nocloseOpen = ref(false)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VModal'
})
useHead({
title: 'VModal - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'VModal',
to: '/components/modal',
},
]"
/>
<div class="columns is-multiline">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<!--VModal-->
<VModalBaseDocumentation />
<div class="buttons mb-6">
<VButton
bold
@click="leftActionsOpen = true"
>
Left Actions
</VButton>
<VButton
bold
@click="centeredActionsOpen = true"
>
Center Actions
</VButton>
<VButton
bold
@click="rightActionsOpen = true"
>
Right Actions
</VButton>
<VButton
bold
@click="customLabelActionsOpen = true"
>
Custom label
</VButton>
</div>
<!--VModal sizes-->
<VModalSizesDocumentation />
<div class="buttons mb-6">
<VButton
bold
@click="smallOpen = true"
>
Small Modal
</VButton>
<VButton
bold
@click="standardOpen = true"
>
Standard Modal
</VButton>
<VButton
bold
@click="mediumOpen = true"
>
Medium Modal
</VButton>
<VButton
bold
@click="largeOpen = true"
>
Large Modal
</VButton>
<VButton
bold
@click="bigOpen = true"
>
Big Modal
</VButton>
</div>
<!--VModal forms-->
<VModalFormsDocumentation />
<div class="buttons mb-6">
<VButton
bold
@click="smallFormOpen = true"
>
Small Modal
</VButton>
<VButton
bold
@click="standardFormOpen = true"
>
Standard Modal
</VButton>
<VButton
bold
@click="mediumFormOpen = true"
>
Medium Modal
</VButton>
<VButton
bold
@click="largeFormOpen = true"
>
Large Modal
</VButton>
<VButton
bold
@click="bigFormOpen = true"
>
Big Modal
</VButton>
</div>
<!--VModal options-->
<VModalOptionsDocumentation />
<div class="buttons mb-6">
<VButton
bold
@click="noscrollOpen = true"
>
Scroll disabled
</VButton>
<VButton
bold
@click="nocloseOpen = true"
>
No background close handler
</VButton>
</div>
<DocumentationMeta
name="VModal"
:meta="VModalMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
<VModal
title="Standard actions"
:open="leftActionsOpen"
@close="leftActionsOpen = false"
>
<template #content>
<VPlaceholderSection
title="Go Premium"
subtitle="Unlock more features and business tools by going premium"
/>
</template>
<template #action="{ close }">
<VButton
color="primary"
raised
@click="close()"
>
Confirm
</VButton>
</template>
</VModal>
<VModal
title="Centered actions"
:open="centeredActionsOpen"
actions="center"
@close="centeredActionsOpen = false"
>
<template #content>
<VPlaceholderSection
title="Go Premium"
subtitle="Unlock more features and business tools by going premium"
/>
</template>
<template #action="{ close }">
<VButton
color="primary"
raised
@click="close()"
>
Confirm
</VButton>
</template>
</VModal>
<VModal
title="Right actions"
:open="rightActionsOpen"
actions="right"
@close="rightActionsOpen = false"
>
<template #content>
<VPlaceholderSection
title="Go Premium"
subtitle="Unlock more features and business tools by going premium"
/>
</template>
<template #action="{ close }">
<VButton
color="primary"
raised
@click="close()"
>
Confirm
</VButton>
</template>
</VModal>
<VModal
title="Custom Label"
:open="customLabelActionsOpen"
actions="right"
cancel-label="Return"
@close="customLabelActionsOpen = false"
>
<template #content>
<VPlaceholderSection
title="Go Premium"
subtitle="Unlock more features and business tools by going premium"
/>
</template>
</VModal>
<VModal
:open="smallOpen"
title="Invitation"
size="small"
actions="center"
rounded
@close="smallOpen = false"
>
<template #content>
<VPlaceholderSection
title="You were invited"
subtitle="Jimmy H. invited you to join the Heartman &amp; Sons project."
>
<template #image>
<VAvatar
picture="https://media.cssninja.io/content/avatars/22.jpg"
badge="/images/icons/flags/united-states-of-america.svg"
/>
</template>
</VPlaceholderSection>
</template>
<template #action="{ close }">
<VButton
color="primary"
rounded
raised
@click="close()"
>
Confirm
</VButton>
</template>
</VModal>
<VModal
:open="standardOpen"
title="Invitation"
actions="center"
rounded
@close="standardOpen = false"
>
<template #content>
<VPlaceholderSection
title="You were invited"
subtitle="Jimmy H. invited you to join the Heartman &amp; Sons project."
>
<template #image>
<VAvatar
picture="https://media.cssninja.io/content/avatars/22.jpg"
badge="/images/icons/flags/united-states-of-america.svg"
/>
</template>
</VPlaceholderSection>
</template>
<template #action="{ close }">
<VButton
color="primary"
rounded
raised
@click="close()"
>
Confirm
</VButton>
</template>
</VModal>
<VModal
:open="mediumOpen"
title="Invitation"
size="medium"
actions="center"
rounded
@close="mediumOpen = false"
>
<template #content>
<VPlaceholderSection
title="You were invited"
subtitle="Jimmy H. invited you to join the Heartman &amp; Sons project."
>
<template #image>
<VAvatar
picture="https://media.cssninja.io/content/avatars/22.jpg"
badge="/images/icons/flags/united-states-of-america.svg"
/>
</template>
</VPlaceholderSection>
</template>
<template #action="{ close }">
<VButton
color="primary"
rounded
raised
@click="close()"
>
Confirm
</VButton>
</template>
</VModal>
<VModal
:open="largeOpen"
title="Invitation"
size="large"
actions="center"
rounded
@close="largeOpen = false"
>
<template #content>
<VPlaceholderSection
title="You were invited"
subtitle="Jimmy H. invited you to join the Heartman &amp; Sons project."
>
<template #image>
<VAvatar
picture="https://media.cssninja.io/content/avatars/22.jpg"
badge="/images/icons/flags/united-states-of-america.svg"
/>
</template>
</VPlaceholderSection>
</template>
<template #action="{ close }">
<VButton
color="primary"
rounded
raised
@click="close()"
>
Confirm
</VButton>
</template>
</VModal>
<VModal
:open="bigOpen"
title="Invitation"
size="big"
actions="center"
rounded
@close="bigOpen = false"
>
<template #content>
<VPlaceholderSection
title="You were invited"
subtitle="Jimmy H. invited you to join the Heartman &amp; Sons project."
>
<template #image>
<VAvatar
picture="https://media.cssninja.io/content/avatars/22.jpg"
badge="/images/icons/flags/united-states-of-america.svg"
/>
</template>
</VPlaceholderSection>
</template>
<template #action="{ close }">
<VButton
color="primary"
rounded
raised
@click="close()"
>
Confirm
</VButton>
</template>
</VModal>
<VModal
is="form"
method="post"
novalidate
:open="smallFormOpen"
title="Leave a Comment"
size="small"
actions="right"
@submit.prevent="smallFormOpen = false"
@close="smallFormOpen = false"
>
<template #content>
<div class="modal-form">
<VField label="Username *">
<VControl>
<VInput
type="text"
placeholder="Username"
/>
</VControl>
</VField>
<VField label="Email *">
<VControl>
<VInput
type="text"
placeholder="Email Address"
/>
</VControl>
</VField>
<VField label="Comment *">
<VControl>
<VTextarea
rows="4"
placeholder="Your message..."
/>
</VControl>
</VField>
</div>
</template>
<template #action>
<VButton
type="submit"
color="primary"
raised
>
Publish
</VButton>
</template>
</VModal>
<VModal
is="form"
method="post"
novalidate
:open="standardFormOpen"
title="Leave a Comment"
actions="right"
@submit.prevent="standardFormOpen = false"
@close="standardFormOpen = false"
>
<template #content>
<div class="modal-form">
<VField label="Username *">
<VControl>
<VInput
type="text"
placeholder="Username"
/>
</VControl>
</VField>
<VField label="Email *">
<VControl>
<VInput
type="text"
placeholder="Email Address"
/>
</VControl>
</VField>
<VField label="Comment *">
<VControl>
<VTextarea
rows="4"
placeholder="Your message..."
/>
</VControl>
</VField>
</div>
</template>
<template #action>
<VButton
type="submit"
color="primary"
raised
>
Publish
</VButton>
</template>
</VModal>
<VModal
is="form"
method="post"
novalidate
:open="mediumFormOpen"
size="medium"
title="Horizontal Form"
actions="right"
@submit.prevent="mediumFormOpen = false"
@close="mediumFormOpen = false"
>
<template #content>
<div class="modal-form">
<VField
label="Username *"
horizontal
>
<VControl expanded>
<VInput
type="text"
placeholder="Username"
/>
</VControl>
</VField>
<VField
label="Email *"
horizontal
>
<VControl expanded>
<VInput
type="text"
placeholder="Username"
/>
</VControl>
</VField>
<VField
label="Comment *"
horizontal
>
<VControl expanded>
<VTextarea
rows="4"
placeholder="Your message..."
/>
</VControl>
</VField>
</div>
</template>
<template #action>
<VButton
type="submit"
color="primary"
raised
>
Publish
</VButton>
</template>
</VModal>
<VModal
is="form"
method="post"
novalidate
:open="largeFormOpen"
size="large"
title="Create Project"
actions="right"
@submit.prevent="largeFormOpen = false"
@close="largeFormOpen = false"
>
<template #content>
<div class="modal-form">
<div class="columns is-multiline">
<div class="column is-12">
<VField label="Project Name *">
<VControl>
<VInput
type="text"
placeholder="Ex: A cool project"
/>
</VControl>
</VField>
</div>
<div class="column is-6">
<VField label="Project Budget *">
<VControl>
<VInput
type="text"
placeholder="Ex: $3,500"
/>
</VControl>
</VField>
</div>
<div class="column is-6">
<VField label="Project URL *">
<VControl>
<VInput
type="text"
class="VInput"
placeholder="Ex: https://project.com"
/>
</VControl>
</VField>
</div>
<div class="column is-12">
<VField label="Description *">
<VControl>
<VTextarea
rows="3"
placeholder="Details about the project..."
/>
</VControl>
</VField>
</div>
</div>
</div>
</template>
<template #action>
<VButton
type="submit"
color="primary"
raised
>
Save Changes
</VButton>
</template>
</VModal>
<VModal
is="form"
:open="bigFormOpen"
size="large"
title="Create Project"
actions="right"
@submit.prevent="bigFormOpen = false"
@close="bigFormOpen = false"
>
<template #content>
<div class="modal-form">
<div class="columns is-multiline">
<div class="column is-12">
<VField label="Project Name *">
<VControl>
<VInput
type="text"
placeholder="Ex: A cool project"
/>
</VControl>
</VField>
</div>
<div class="column is-6">
<VField
class="is-image-select"
label="Project Member"
>
<VControl>
<Multiselect
placeholder="Select employees"
track-by="name"
label="name"
:search="true"
:options="[
{
value: 'alice',
name: 'Alice Carasca',
image: 'https://media.cssninja.io/content/avatars/7.jpg',
},
{
value: 'erik',
name: 'Erik Kovalsky',
image: '/images/avatars/svg/vuero-1.svg',
},
{
value: 'melany',
name: 'melany Wallace',
image: 'https://media.cssninja.io/content/avatars/25.jpg',
},
{
value: 'tara',
name: 'Tara Svenson',
image: 'https://media.cssninja.io/content/avatars/13.jpg',
},
{
value: 'mary',
name: 'Mary Lebowski',
image: 'https://media.cssninja.io/content/avatars/5.jpg',
},
{
value: 'irina',
name: 'Irina Vierbovsky',
image: 'https://media.cssninja.io/content/avatars/23.jpg',
},
{
value: 'jonathan',
name: 'Jonathan Krugger',
image: 'https://media.cssninja.io/content/avatars/32.jpg',
},
]"
:max-height="145"
>
<template #singlelabel="{ value }">
<div class="multiselect-single-label">
<img
class="select-label-icon"
:src="value.image"
alt=""
>
{{ value.name }}
</div>
</template>
<template #option="{ option }">
<img
class="select-option-icon"
:src="option.image"
alt=""
>
{{ option.name }}
</template>
</Multiselect>
</VControl>
</VField>
</div>
<div class="column is-6">
<VField
class="is-image-select"
label="Project Type *"
>
<VControl>
<Multiselect
placeholder="Select language"
track-by="name"
label="name"
:search="true"
:options="[
{
value: 'javascript',
name: 'Javascript',
image: '/images/icons/stacks/js.svg',
},
{
value: 'reactjs',
name: 'ReactJS',
image: '/images/icons/stacks/reactjs.svg',
},
{
value: 'vuejs',
name: 'VueJS',
image: '/images/icons/stacks/vuejs.svg',
},
]"
:max-height="145"
>
<template #singlelabel="{ value }">
<div class="multiselect-single-label">
<img
class="select-label-icon"
:src="value.image"
alt=""
>
{{ value.name }}
</div>
</template>
<template #option="{ option }">
<img
class="select-option-icon"
:src="option.image"
alt=""
>
{{ option.name }}
</template>
</Multiselect>
</VControl>
</VField>
</div>
<div class="column is-6">
<VField label="Project Budget *">
<VControl>
<VInput
type="text"
placeholder="Ex: $3,500"
/>
</VControl>
</VField>
</div>
<div class="column is-6">
<VField label="Project URL *">
<VControl>
<VInput
type="text"
class="VInput"
placeholder="Ex: https://project.com"
/>
</VControl>
</VField>
</div>
<div class="column is-12">
<VField label="Description *">
<VControl>
<VTextarea
rows="3"
placeholder="Details about the project..."
/>
</VControl>
</VField>
</div>
</div>
</div>
</template>
<template #action>
<VButton
type="submit"
color="primary"
raised
>
Save Changes
</VButton>
</template>
</VModal>
<VModal
:open="noscrollOpen"
title="Invitation"
size="small"
actions="center"
noscroll
@close="noscrollOpen = false"
>
<template #content>
<VPlaceholderSection
title="Go Premium"
subtitle="Unlock more features and business tools by going premium"
/>
</template>
<template #action="{ close }">
<VButton
color="primary"
raised
@click="close()"
>
Confirm
</VButton>
</template>
</VModal>
<VModal
:open="nocloseOpen"
title="Invitation"
size="small"
actions="center"
noclose
@close="nocloseOpen = false"
>
<template #content>
<VPlaceholderSection
title="Go Premium"
subtitle="Unlock more features and business tools by going premium"
/>
</template>
<template #action="{ close }">
<VButton
color="primary"
raised
@click="close()"
>
Confirm
</VButton>
</template>
</VModal>
</div>
</template>

View File

@@ -0,0 +1,77 @@
<script setup lang="ts">
import { VFlexPaginationMeta } from '/@src/data/documentation/components-meta'
const route = useRoute()
const currentPage = computed(() => {
try {
return Number.parseInt(route.query.page as string) || 1
}
catch {
// discard
}
return 1
})
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VFlexPagination'
})
useHead({
title: 'VFlexPagination - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'VFlexPagination',
to: '/components/pagination',
},
]"
/>
<div class="columns">
<div class="column is-12">
<!--Flex Table-->
<VFlexPaginationBaseDocumentation />
<div class="mt-4">
<VFlexPagination
:item-per-page="8"
:total-items="512"
:current-page="currentPage"
/>
</div>
<div class="mt-4 mb-6">
<VFlexPagination
:item-per-page="8"
:total-items="512"
:current-page="currentPage"
:max-links-displayed="6"
/>
</div>
<DocumentationMeta
name="VFlexPagination"
:meta="VFlexPaginationMeta"
/>
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,90 @@
<script setup lang="ts">
import { VPlaceholderPageMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VPlaceholderPage'
})
useHead({
title: 'VPlaceholderPage - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'VPlaceholderPage',
to: '/components/placeholder-page',
},
]"
/>
<div class="columns is-multiline">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<VPlaceholderPage
title="We couldn't find any matching results."
subtitle="Too bad. Looks like we couldn't find any matching results for the
search terms you've entered. Please try different search terms or
criteria."
larger
>
<template #image>
<img
class="light-image"
src="/images/illustrations/placeholders/search-1.svg"
alt=""
>
<img
class="dark-image"
src="/images/illustrations/placeholders/search-1-dark.svg"
alt=""
>
</template>
<template #action>
<VButtons align="centered">
<VButton>Go to the catalog</VButton>
<VButton color="primary">
Search everywhere
</VButton>
</VButtons>
</template>
</VPlaceholderPage>
<div class="pt-6">
<DocumentationMeta
name="VPlaceholderPage"
:meta="VPlaceholderPageMeta"
/>
</div>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,92 @@
<script setup lang="ts">
import { VPlaceholderSectionMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VPlaceholderSection'
})
useHead({
title: 'VPlaceholderSection - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'VPlaceholderSection',
to: '/components/placeholder-section',
},
]"
/>
<div class="columns is-multiline">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<div class="columns is-vcentered">
<div class="column is-6">
<VCard>
<VPlaceholderSection
title="You were invited"
subtitle="Jimmy H. invited you to join the Heartman &amp; Sons project."
>
<template #image>
<VAvatar
picture="https://media.cssninja.io/content/avatars/22.jpg"
badge="/images/icons/flags/united-states-of-america.svg"
/>
</template>
</VPlaceholderSection>
</VCard>
</div>
<div class="column is-6">
<VPlaceholderSection
title="Go Premium"
subtitle="Unlock more features and business tools by going premium"
>
<template #action>
<VButtons align="centered">
<VButton color="primary">
View plans
</VButton>
</VButtons>
</template>
</VPlaceholderSection>
</div>
</div>
<DocumentationMeta
name="VPlaceholderSection"
:meta="VPlaceholderSectionMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,65 @@
<script setup lang="ts">
import { VPlaceloadAvatarMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VPlaceloadAvatar'
})
useHead({
title: 'VPlaceloadAvatar - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'VPlaceloadAvatar',
to: '/components/placeload/avatar',
},
]"
/>
<div class="columns">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<!--Loader-->
<PlaceloadAvatarBaseDocumentation />
<PlaceloadAvatarWrapDocumentation />
<PlaceloadAvatarSizeDocumentation />
<PlaceloadAvatarRadiusDocumentation />
<DocumentationMeta
name="VPlaceloadAvatar"
:meta="VPlaceloadAvatarMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,66 @@
<script setup lang="ts">
import { VPlaceloadMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VPlaceload'
})
useHead({
title: 'VPlaceload - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'VPlaceload',
to: '/components/placeload/',
},
]"
/>
<div class="columns">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<!--Loader-->
<PlaceloadBaseDocumentation />
<PlaceloadWrapDocumentation />
<PlaceloadSizeDocumentation />
<DocumentationMeta
name="VPlaceload"
:meta="VPlaceloadMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,62 @@
<script setup lang="ts">
import { VPlaceloadTextMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VPlaceloadText'
})
useHead({
title: 'VPlaceloadText - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'VPlaceloadText',
to: '/components/placeload/text',
},
]"
/>
<div class="columns">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<PlaceloadTextBaseDocumentation />
<PlaceloadTextSizeDocumentation />
<DocumentationMeta
name="VPlaceloadText"
:meta="VPlaceloadTextMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,131 @@
<script setup lang="ts">
import { VBillboardJSMeta } from '/@src/data/documentation/components-meta'
import { useSplineSimple } from '/@src/data/dashboards/billboardjs-demo/splineSimple'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const themeColors = useThemeColors()
const { options } = useSplineSimple()
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VBillboardJS'
})
useHead({
title: 'VBillboardJS - Plugins - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'Plugins',
},
{
label: 'VBillboardJS',
to: '/components/plugins/billboard-js',
},
]"
/>
<div class="columns is-multiline">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<VBillboardJsDocumentation />
<div class="columns is-mutliline">
<div class="column is-6">
<VCard>
<VBillboardJS
:options="{
data: {
x: 'x',
columns: [
['x', 'Data A', 'Data B', 'Data C', 'Data D', 'Data E'],
['data1', 330, 350, 200, 380, 150],
['data2', 130, 100, 30, 200, 80],
['data3', 230, 153, 85, 300, 250],
],
colors: {
data1: themeColors.purple,
data2: themeColors.lime,
data3: themeColors.info,
data4: themeColors.purple,
},
type: 'radar',
labels: true,
},
radar: {
axis: {
max: 400,
},
level: {
depth: 4,
},
direction: {
clockwise: true,
},
},
size: {
height: 280,
},
padding: {
bottom: 20,
},
title: {
text: 'Radar Chart',
position: 'left',
padding: {
bottom: 20,
right: 20,
top: 0,
left: 20,
},
},
legend: {
position: 'inset',
},
}"
/>
</VCard>
</div>
<div class="column is-6">
<VCard type="smooth">
<VBillboardJS :options="options" />
</VCard>
</div>
</div>
<DocumentationMeta
name="VBillboardJS"
:meta="VBillboardJSMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,169 @@
<script setup lang="ts">
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const notyf = useNotyf()
onMounted(() => {
notyf?.success('This toast is displayed when the page is mounted')
})
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'notyf'
})
useHead({
title: 'Notyf - Plugins - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'Plugins',
},
{
label: 'Notif',
to: '/components/plugins/notif',
},
]"
/>
<div class="columns is-multiline">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<!--Success Toast-->
<NotyfSuccessDocumentation />
<div class="buttons mb-6">
<VButton
bold
@click="notyf.primary('Success Toast')"
>
Success Toast
</VButton>
</div>
<!--Error Toast-->
<NotyfErrorDocumentation />
<div class="buttons mb-6">
<VButton
bold
@click="notyf.error('Error Toast')"
>
Error Toast
</VButton>
</div>
<!--Info Toast-->
<NotyfInfoDocumentation />
<div class="buttons mb-6">
<VButton
bold
@click="notyf.info('Info Toast')"
>
Info Toast
</VButton>
</div>
<!--Warning Toast-->
<NotyfWarningDocumentation />
<div class="buttons mb-6">
<VButton
bold
@click="notyf.warning('Warning Toast')"
>
Warning Toast
</VButton>
</div>
<!--Primary Toast-->
<NotyfPrimaryDocumentation />
<div class="buttons mb-6">
<VButton
bold
@click="notyf.primary('Primary Toast')"
>
Primary Toast
</VButton>
</div>
<!--Purple Toast-->
<NotyfPurpleDocumentation />
<div class="buttons mb-6">
<VButton
bold
@click="notyf.purple('Purple Toast')"
>
Purple Toast
</VButton>
</div>
<!--Blue Toast-->
<NotyfBlueDocumentation />
<div class="buttons mb-6">
<VButton
bold
@click="notyf.blue('Blue Toast')"
>
Blue Toast
</VButton>
</div>
<!--Green Toast-->
<NotyfGreenDocumentation />
<div class="buttons mb-6">
<VButton
bold
@click="notyf.green('Green Toast')"
>
Green Toast
</VButton>
</div>
<!--Orange Toast-->
<NotyfOrangeDocumentation />
<div class="buttons mb-6">
<VButton
bold
@click="notyf.orange('Orange Toast')"
>
Orange Toast
</VButton>
</div>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,74 @@
<script setup lang="ts">
import { VPhotosSwipeMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VPhotosSwipe'
})
useHead({
title: 'VPhotosSwipe - Plugins - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'Plugins',
},
{
label: 'VPhotosSwipe',
to: '/components/plugins/photos-swipe',
},
]"
/>
<div class="columns is-multiline">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<!--Thumbnails-->
<GalleryImagesDocumentation />
<!--Curved Thumbnails-->
<GalleryImagesCurvedDocumentation />
<!--Rounded Thumbnails-->
<GalleryImagesRoundedDocumentation />
<!--Bigger Thumbnails-->
<GalleryImagesLargerDocumentation />
<DocumentationMeta
name="VPhotosSwipe"
:meta="VPhotosSwipeMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,71 @@
<script setup lang="ts">
import { VPlyrMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VPlyr'
})
useHead({
title: 'VPlyr - Plugins - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'Plugins',
},
{
label: 'VPlyr',
to: '/components/plugins/plyr',
},
]"
/>
<div class="columns is-multiline">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<!--1:1 Video Player-->
<VideoBaseDocumentation />
<!--4:3 Video Player-->
<VideoFourDocumentation />
<!--16:9 Video Player-->
<VideoSixteenDocumentation />
<DocumentationMeta
name="VPlyr"
:meta="VPlyrMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,70 @@
<script setup lang="ts">
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'Tippy'
})
useHead({
title: 'Tippy - Plugins - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'Plugins',
},
{
label: 'Tippy',
to: '/components/plugins/tippy',
},
]"
/>
<div class="columns is-multiline">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<!--Text Popover-->
<PopoverBaseDocumentation />
<!--Popover Position-->
<PopoverPositionDocumentation />
<!--Avatar Popover-->
<PopoverAvatarDocumentation />
<!--Icon Popover-->
<PopoverIconDocumentation />
<!--Profile Popover-->
<PopoverComplexDocumentation />
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,68 @@
<script setup lang="ts">
import { VProgressMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VProgress'
})
useHead({
title: 'VProgress - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'VProgess',
to: '/components/progress',
},
]"
/>
<div class="columns">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<!--VProgress-->
<VProgressBaseDocumentation />
<!--VProgress colors-->
<VProgressColorsDocumentation />
<!--VProgress undeterminate-->
<VProgressUndeterminateDocumentation />
<DocumentationMeta
name="VProgress"
:meta="VProgressMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,75 @@
<script setup lang="ts">
import { VRadioMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VRadio'
})
useHead({
title: 'VRadio - Switches Elements - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'Switches',
},
{
label: 'VRadio',
to: '/components/radio',
},
]"
/>
<div class="columns">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<!-- @TODO -->
<!--Outlined Radio-->
<RadioOutlinedDocumentation />
<!--Outlined Square Radio-->
<RadioOutlinedSquareDocumentation />
<!--Radio Solid-->
<RadioSolidDocumentation />
<!--Radio Solid Square-->
<RadioSolidSquareDocumentation />
<DocumentationMeta
name="VRadio"
:meta="VRadioMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,89 @@
<script setup lang="ts">
import { VSnackMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VSnack'
})
useHead({
title: 'VSnack - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'VSnack',
to: '/components/snack',
},
]"
/>
<div class="columns">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<!--VSnack base-->
<VSnackBaseDocumentation />
<!--VSnack white-->
<VSnackWhiteDocumentation />
<!--VSnack feather-->
<VSnackFeatherDocumentation />
<!--VSnack feather solid-->
<VSnackFeatherSolidDocumentation />
<!--VSnack fa-->
<VSnackFaDocumentation />
<!--VSnack fa solid-->
<VSnackFaSolidDocumentation />
<!--VSnack line-->
<VSnackLineDocumentation />
<!--VSnack line solid-->
<VSnackLineSolidDocumentation />
<!--VSnack small image-->
<VSnackSmallImageDocumentation />
<!--VSnack small icon-->
<VSnackSmallIconDocumentation />
<DocumentationMeta
name="VSnack"
:meta="VSnackMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,74 @@
<script setup lang="ts">
import { VSwitchBlockMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VSwitchBlock'
})
useHead({
title: 'VSwitchBlock - Switches Elements - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'Switches',
},
{
label: 'VSwitchBlock',
to: '/components/switch-block',
},
]"
/>
<div class="columns">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<!--Switch-->
<SwitchBlockDocumentation />
<!--Thin Switch-->
<SwitchBlockThinDocumentation />
<!--Switch Block-->
<SwitchBlockLabelDocumentation />
<!--Switch Block Thin-->
<SwitchBlockThinLabelDocumentation />
<DocumentationMeta
name="VSwitchBlock"
:meta="VSwitchBlockMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,68 @@
<script setup lang="ts">
import { VSwitchSegmentMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VSwitchSegment'
})
useHead({
title: 'VSwitchSegment - Switches Elements - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'Switches',
},
{
label: 'VSwitchSegment',
to: '/components/switch-segment',
},
]"
/>
<div class="columns">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<!--Switch-->
<SwitchSegmentDocumentation />
<!--Label-->
<SwitchSegmentLabelDocumentation />
<DocumentationMeta
name="VSwitchSegment"
:meta="VSwitchSegmentMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,335 @@
<script setup lang="ts">
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'Table'
})
useHead({
title: 'Table - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'Table',
to: '/components/table',
},
]"
/>
<div class="columns">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<!--Table-->
<TableBaseDocumentation />
<VCard
radius="smooth"
class="demo-table mb-6"
>
<table
class="table is-hoverable is-fullwidth"
aria-label="Vuero basic table example"
>
<thead>
<tr>
<th scope="col">
First Name
</th>
<th scope="col">
Last Name
</th>
<th scope="col">
Position
</th>
<th
scope="col"
class="is-end"
>
<div class="dark-inverted is-flex is-justify-content-flex-end">
Actions
</div>
</th>
</tr>
</thead>
<tbody>
<tr>
<td>Tina</td>
<td>Bergmann</td>
<td>Head of Sales</td>
<td class="is-end">
<div class="is-flex is-justify-content-flex-end">
<FlexTableDropdown />
</div>
</td>
</tr>
<tr>
<td>John</td>
<td>Wistmus</td>
<td>Senior Executive</td>
<td class="is-end">
<div class="is-flex is-justify-content-flex-end">
<FlexTableDropdown />
</div>
</td>
</tr>
<tr>
<td>Sam</td>
<td>Watson</td>
<td>Software Engineer</td>
<td class="is-end">
<div class="is-flex is-justify-content-flex-end">
<FlexTableDropdown />
</div>
</td>
</tr>
<tr>
<td>Jolaine</td>
<td>Joestar</td>
<td>HR Manager</td>
<td class="is-end">
<div class="is-flex is-justify-content-flex-end">
<FlexTableDropdown />
</div>
</td>
</tr>
<tr>
<td>Anders</td>
<td>Jensen</td>
<td>Accountant</td>
<td class="is-end">
<div class="is-flex is-justify-content-flex-end">
<FlexTableDropdown />
</div>
</td>
</tr>
</tbody>
</table>
</VCard>
<!--Table striped-->
<TableStripedDocumentation />
<VCard
radius="smooth"
class="demo-table"
>
<table
class="table is-striped is-fullwidth"
aria-label="Vuero striped table example"
>
<thead>
<tr>
<th scope="col">
First Name
</th>
<th scope="col">
Last Name
</th>
<th scope="col">
Position
</th>
<th
scope="col"
class="is-end"
>
<div class="dark-inverted is-flex is-justify-content-flex-end">
Actions
</div>
</th>
</tr>
</thead>
<tbody>
<tr>
<td>Tina</td>
<td>Bergmann</td>
<td>Head of Sales</td>
<td class="is-end">
<div class="is-flex is-justify-content-flex-end">
<FlexTableDropdown />
</div>
</td>
</tr>
<tr>
<td>John</td>
<td>Wistmus</td>
<td>Senior Executive</td>
<td class="is-end">
<div class="is-flex is-justify-content-flex-end">
<FlexTableDropdown />
</div>
</td>
</tr>
<tr>
<td>Sam</td>
<td>Watson</td>
<td>Software Engineer</td>
<td class="is-end">
<div class="is-flex is-justify-content-flex-end">
<FlexTableDropdown />
</div>
</td>
</tr>
<tr>
<td>Jolaine</td>
<td>Joestar</td>
<td>HR Manager</td>
<td class="is-end">
<div class="is-flex is-justify-content-flex-end">
<FlexTableDropdown />
</div>
</td>
</tr>
<tr>
<td>Anders</td>
<td>Jensen</td>
<td>Accountant</td>
<td class="is-end">
<div class="is-flex is-justify-content-flex-end">
<FlexTableDropdown />
</div>
</td>
</tr>
</tbody>
</table>
</VCard>
<!--Table media-->
<TableMediaDocumentation />
<VCard
radius="smooth"
class="demo-table"
>
<table
class="table is-hoverable is-fullwidth"
aria-label="Vuero media table example"
>
<thead>
<tr>
<th
scope="col"
class="is-media"
/>
<th scope="col">
First Name
</th>
<th scope="col">
Last Name
</th>
<th scope="col">
Position
</th>
<th
scope="col"
class="is-end"
>
<div class="dark-inverted is-flex is-justify-content-flex-end">
Actions
</div>
</th>
</tr>
</thead>
<tbody>
<tr>
<td class="is-media">
<VAvatar picture="https://media.cssninja.io/content/avatars/8.gif" />
</td>
<td>Tina</td>
<td>Bergmann</td>
<td>Head of Sales</td>
<td class="is-end">
<div class="is-flex is-justify-content-flex-end">
<FlexTableDropdown />
</div>
</td>
</tr>
<tr>
<td class="is-media">
<VAvatar picture="https://media.cssninja.io/content/avatars/1.gif" />
</td>
<td>John</td>
<td>Wistmus</td>
<td>Senior Executive</td>
<td class="is-end">
<div class="is-flex is-justify-content-flex-end">
<FlexTableDropdown />
</div>
</td>
</tr>
<tr>
<td class="is-media">
<VAvatar picture="https://media.cssninja.io/content/avatars/4.gif" />
</td>
<td>Sam</td>
<td>Watson</td>
<td>Software Engineer</td>
<td class="is-end">
<div class="is-flex is-justify-content-flex-end">
<FlexTableDropdown />
</div>
</td>
</tr>
<tr>
<td class="is-media">
<VAvatar
initials="JD"
color="info"
/>
</td>
<td>Jolaine</td>
<td>Joestar</td>
<td>HR Manager</td>
<td class="is-end">
<div class="is-flex is-justify-content-flex-end">
<FlexTableDropdown />
</div>
</td>
</tr>
<tr>
<td class="is-media">
<VAvatar picture="https://media.cssninja.io/content/avatars/3.gif" />
</td>
<td>Anders</td>
<td>Jensen</td>
<td>Accountant</td>
<td class="is-end">
<div class="is-flex is-justify-content-flex-end">
<FlexTableDropdown />
</div>
</td>
</tr>
</tbody>
</table>
</VCard>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,92 @@
<script setup lang="ts">
import { VTabsMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VTabs'
})
useHead({
title: 'VTabs - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'VTabs',
to: '/components/tabs',
},
]"
/>
<div class="columns">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<!--VTabs-->
<TabsBaseDocumentation />
<!--Centered Tabs-->
<TabsCenteredDocumentation />
<!--Right Tabs-->
<TabsRightDocumentation />
<!--Iconify Tabs-->
<TabsIconifyDocumentation />
<!--Boxed Tabs-->
<TabsBoxedDocumentation />
<!--Toggle Tabs-->
<TabsToggleDocumentation />
<!--Rounded Toggle Tabs-->
<TabsRoundedDocumentation />
<!--Slider Tabs 2x-->
<TabsSliderDoubleDocumentation />
<!--Square Slider Tabs 2x-->
<TabsSliderDsquareDocumentation />
<!--Slider Tabs 3x-->
<TabsSliderTripleDocumentation />
<!--Squared Slider Tabs 3x-->
<TabsSliderTsquareDocumentation />
<DocumentationMeta
name="VTabs"
:meta="VTabsMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,84 @@
<script setup lang="ts">
import { VTagMeta, VTagsMeta } from '/@src/data/documentation/components-meta'
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VTag'
})
useHead({
title: 'VTag - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'VTag',
to: '/components/tag',
},
]"
/>
<div class="columns">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<!--VTag base-->
<VTagBaseDocumentation />
<!--VTag rounded-->
<VTagRoundedDocumentation />
<!--VTag curved-->
<VTagCurvedDocumentation />
<!--VTag Outlined-->
<VTagOutlinedDocumentation />
<!--VTag Elevated-->
<VTagElevatedDocumentation />
<!--VTag addons-->
<VTagAddonsDocumentation />
<!--VTag list-->
<VTagListDocumentation />
<DocumentationMeta
name="VTag"
:meta="VTagMeta"
/>
<DocumentationMeta
name="VTags"
:meta="VTagsMeta"
/>
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,62 @@
<script setup lang="ts">
const markdownContainer = ref<HTMLElement>()
const toc = useMarkdownToc(markdownContainer)
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = 'VTooltip'
})
useHead({
title: 'VTooltip - Components - Vuero',
})
</script>
<template>
<div>
<VBreadcrumb
with-icons
separator="bullet"
:items="[
{
label: 'Vuero',
hideLabel: true,
icon: 'lucide:home',
to: '/',
},
{
label: 'Components',
to: '/components/',
},
{
label: 'VTooltip',
to: '/components/tooltip',
},
]"
/>
<div class="columns">
<div
ref="markdownContainer"
:class="[toc.length > 0 ? 'is-9' : 'is-12']"
class="column doc-column stay-focus-container"
>
<!-- @TODO -->
<!--Default Tooltip-->
<TooltipBaseDocumentation />
<!--Tooltip Colors-->
<TooltipColorsDocumentation />
<!--Tooltip Shapes-->
<TooltipShapesDocumentation />
</div>
<div
v-if="toc.length"
class="column is-3 toc-column"
>
<DocumentationToc :toc="toc" />
</div>
</div>
</div>
</template>

5
src/pages/error.vue Normal file
View File

@@ -0,0 +1,5 @@
<template>
<MinimalLayout>
<RouterView />
</MinimalLayout>
</template>

155
src/pages/error/page-1.vue Normal file
View File

@@ -0,0 +1,155 @@
<script setup lang="ts">
useHead({
title: 'Error Page 1 - Vuero',
})
const router = useRouter()
</script>
<template>
<div class="error-container">
<div class="error-nav">
<VDarkmodeToggle />
</div>
<div class="error-wrapper">
<div class="error-inner has-text-centered">
<div class="bg-number dark-inverted">
404
</div>
<SVGErrorPlaceholder />
<h3 class="dark-inverted">
We couldn't find that page
</h3>
<p>
Looks like we couldn't find that page. Please try again or contact an
administrator if the problem persists.
</p>
<div class="button-wrap">
<VButton
color="primary"
elevated
@click="() => router.go(-1)"
>
Take me Back
</VButton>
</div>
</div>
</div>
</div>
</template>
<style lang="scss">
.error-container {
width: 100vw;
min-height: 100vh;
.error-nav {
.dark-mode {
position: absolute;
inset-inline-end: 0;
top: 0;
display: inline-block;
transform: scale(0.5);
}
}
.error-wrapper {
max-width: 840px;
margin: 0 auto;
padding-top: 40px;
.error-inner {
position: relative;
max-width: 540px;
margin: 0 auto;
.bg-number {
font-family: var(--font);
position: absolute;
top: -58px;
inset-inline-start: -50px;
inset-inline-end: 0;
margin: 0 auto;
font-size: 28rem;
font-weight: 600;
opacity: 0.15;
z-index: 0;
}
img,
svg,
h3,
p,
.button-wrap {
position: relative;
z-index: 1;
}
img,
.iconify {
display: block;
max-width: 100%;
margin: 0 auto;
}
h3 {
font-size: 1.5rem;
font-family: var(--font-alt);
color: var(--dark-text);
font-weight: 600;
margin-top: 10px;
}
p {
font-family: var(--font);
font-size: 1.1rem;
margin-bottom: 16px;
}
.button-wrap {
.button {
min-width: 220px;
min-height: 50px;
}
}
}
}
}
.is-dark {
.error-container {
.error-wrapper {
.error-inner {
.bg-number {
opacity: 0.09;
}
}
}
}
}
@media only screen and (width <= 767px) {
.error-container {
.error-wrapper {
padding-top: 60px;
.error-inner {
padding: 10px;
.bg-number {
top: -35px;
inset-inline-start: -18px;
inset-inline-end: 0;
font-size: 13rem;
}
img,
.iconify {
max-width: 345px;
}
}
}
}
}
</style>

Some files were not shown because too many files have changed in this diff Show More