Implemented Timeline
parent
11bd7f029c
commit
ee0e002e26
|
@ -38,6 +38,10 @@
|
|||
"name": "Textarea",
|
||||
"to": "/textarea"
|
||||
},
|
||||
{
|
||||
"name": "Timeline",
|
||||
"to": "/timeline"
|
||||
},
|
||||
{
|
||||
"name": "ToggleSwitch",
|
||||
"to": "/toggleswitch"
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
<template>
|
||||
<DocSectionText v-bind="$attrs">
|
||||
<p>Content location relative the line is defined with the <i>align</i> property.</p>
|
||||
</DocSectionText>
|
||||
<div class="card flex flex-wrap gap-12">
|
||||
<Timeline :value="events" class="w-full md:w-80">
|
||||
<template #content="slotProps">
|
||||
{{ slotProps.item.status }}
|
||||
</template>
|
||||
</Timeline>
|
||||
|
||||
<Timeline :value="events" align="right" class="w-full md:w-80">
|
||||
<template #content="slotProps">
|
||||
{{ slotProps.item.status }}
|
||||
</template>
|
||||
</Timeline>
|
||||
|
||||
<Timeline :value="events" align="alternate" class="w-full md:w-80">
|
||||
<template #content="slotProps">
|
||||
{{ slotProps.item.status }}
|
||||
</template>
|
||||
</Timeline>
|
||||
</div>
|
||||
<DocSectionCode :code="code" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Timeline from '@/plex/timeline';
|
||||
import { ref } from 'vue';
|
||||
|
||||
const events = ref([
|
||||
{ status: 'Ordered', date: '15/10/2020 10:30', icon: 'pi pi-shopping-cart', color: '#9C27B0' },
|
||||
{ status: 'Processing', date: '15/10/2020 14:00', icon: 'pi pi-cog', color: '#673AB7' },
|
||||
{ status: 'Shipped', date: '15/10/2020 16:15', icon: 'pi pi-shopping-cart', color: '#FF9800' },
|
||||
{ status: 'Delivered', date: '16/10/2020 10:00', icon: 'pi pi-check', color: '#607D8B' }
|
||||
]);
|
||||
|
||||
const code = ref(`
|
||||
<template>
|
||||
<div class="card flex flex-wrap gap-12">
|
||||
<Timeline :value="events" class="w-full md:w-80">
|
||||
<template #content="slotProps">
|
||||
{{ slotProps.item.status }}
|
||||
</template>
|
||||
</Timeline>
|
||||
|
||||
<Timeline :value="events" align="right" class="w-full md:w-80">
|
||||
<template #content="slotProps">
|
||||
{{ slotProps.item.status }}
|
||||
</template>
|
||||
</Timeline>
|
||||
|
||||
<Timeline :value="events" align="alternate" class="w-full md:w-80">
|
||||
<template #content="slotProps">
|
||||
{{ slotProps.item.status }}
|
||||
</template>
|
||||
</Timeline>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Timeline from '@/plex/timeline';
|
||||
import { ref } from 'vue';
|
||||
|
||||
const events = ref([
|
||||
{ status: 'Ordered', date: '15/10/2020 10:30', icon: 'pi pi-shopping-cart', color: '#9C27B0' },
|
||||
{ status: 'Processing', date: '15/10/2020 14:00', icon: 'pi pi-cog', color: '#673AB7' },
|
||||
{ status: 'Shipped', date: '15/10/2020 16:15', icon: 'pi pi-shopping-cart', color: '#FF9800' },
|
||||
{ status: 'Delivered', date: '16/10/2020 10:00', icon: 'pi pi-check', color: '#607D8B' }
|
||||
]);
|
||||
<\/script>
|
||||
`);
|
||||
</script>
|
|
@ -0,0 +1,49 @@
|
|||
<template>
|
||||
<DocSectionText v-bind="$attrs">
|
||||
<p>Timeline requires a <i>value</i> for the collection of events and <i>content</i> slot that receives an object as a parameter to return content.</p>
|
||||
</DocSectionText>
|
||||
<div class="card">
|
||||
<Timeline :value="events">
|
||||
<template #content="slotProps">
|
||||
{{ slotProps.item.status }}
|
||||
</template>
|
||||
</Timeline>
|
||||
</div>
|
||||
<DocSectionCode :code="code" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Timeline from '@/plex/timeline';
|
||||
import { ref } from 'vue';
|
||||
|
||||
const events = ref([
|
||||
{ status: 'Ordered', date: '15/10/2020 10:30', icon: 'pi pi-shopping-cart', color: '#9C27B0' },
|
||||
{ status: 'Processing', date: '15/10/2020 14:00', icon: 'pi pi-cog', color: '#673AB7' },
|
||||
{ status: 'Shipped', date: '15/10/2020 16:15', icon: 'pi pi-shopping-cart', color: '#FF9800' },
|
||||
{ status: 'Delivered', date: '16/10/2020 10:00', icon: 'pi pi-check', color: '#607D8B' }
|
||||
]);
|
||||
|
||||
const code = ref(`
|
||||
<template>
|
||||
<div class="card">
|
||||
<Timeline :value="events">
|
||||
<template #content="slotProps">
|
||||
{{ slotProps.item.status }}
|
||||
</template>
|
||||
</Timeline>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Timeline from '@/plex/timeline';
|
||||
import { ref } from 'vue';
|
||||
|
||||
const events = ref([
|
||||
{ status: 'Ordered', date: '15/10/2020 10:30', icon: 'pi pi-shopping-cart', color: '#9C27B0' },
|
||||
{ status: 'Processing', date: '15/10/2020 14:00', icon: 'pi pi-cog', color: '#673AB7' },
|
||||
{ status: 'Shipped', date: '15/10/2020 16:15', icon: 'pi pi-shopping-cart', color: '#FF9800' },
|
||||
{ status: 'Delivered', date: '16/10/2020 10:00', icon: 'pi pi-check', color: '#607D8B' }
|
||||
]);
|
||||
<\/script>
|
||||
`);
|
||||
</script>
|
|
@ -0,0 +1,65 @@
|
|||
<template>
|
||||
<DocSectionText v-bind="$attrs">
|
||||
<p>TimeLine orientation is controlled with the <i>layout</i> property, default is <i>vertical</i> having <i>horizontal</i> as the alternative.</p>
|
||||
</DocSectionText>
|
||||
<div class="card flex flex-col gap-4">
|
||||
<Timeline :value="events" layout="horizontal" align="top">
|
||||
<template #content="slotProps">
|
||||
{{ slotProps.item }}
|
||||
</template>
|
||||
</Timeline>
|
||||
|
||||
<Timeline :value="events" layout="horizontal" align="bottom">
|
||||
<template #content="slotProps">
|
||||
{{ slotProps.item }}
|
||||
</template>
|
||||
</Timeline>
|
||||
|
||||
<Timeline :value="events" layout="horizontal" align="alternate">
|
||||
<template #opposite> </template>
|
||||
<template #content="slotProps">
|
||||
{{ slotProps.item }}
|
||||
</template>
|
||||
</Timeline>
|
||||
</div>
|
||||
<DocSectionCode :code="code" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Timeline from '@/plex/timeline';
|
||||
import { ref } from 'vue';
|
||||
|
||||
const events = ref(['2020', '2021', '2022', '2023']);
|
||||
|
||||
const code = ref(`
|
||||
<template>
|
||||
<div class="card flex flex-col gap-4">
|
||||
<Timeline :value="events" layout="horizontal" align="top">
|
||||
<template #content="slotProps">
|
||||
{{ slotProps.item }}
|
||||
</template>
|
||||
</Timeline>
|
||||
|
||||
<Timeline :value="events" layout="horizontal" align="bottom">
|
||||
<template #content="slotProps">
|
||||
{{ slotProps.item }}
|
||||
</template>
|
||||
</Timeline>
|
||||
|
||||
<Timeline :value="events" layout="horizontal" align="alternate">
|
||||
<template #opposite> </template>
|
||||
<template #content="slotProps">
|
||||
{{ slotProps.item }}
|
||||
</template>
|
||||
</Timeline>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Timeline from '@/plex/timeline';
|
||||
import { ref } from 'vue';
|
||||
|
||||
const events = ref(['2020', '2021', '2022', '2023']);
|
||||
<\/script>
|
||||
`);
|
||||
</script>
|
|
@ -0,0 +1,16 @@
|
|||
<template>
|
||||
<DocSectionText v-bind="$attrs" />
|
||||
<DocSectionCode :code="code" lang="script" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
code: `
|
||||
import Timeline from '@/plex/timeline';
|
||||
`
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1,55 @@
|
|||
<template>
|
||||
<DocSectionText v-bind="$attrs">
|
||||
<p>Additional content at the other side of the line can be provided with the <i>opposite</i> property.</p>
|
||||
</DocSectionText>
|
||||
<div class="card">
|
||||
<Timeline :value="events">
|
||||
<template #opposite="slotProps">
|
||||
<small class="text-surface-500 dark:text-surface-400">{{ slotProps.item.date }}</small>
|
||||
</template>
|
||||
<template #content="slotProps">
|
||||
{{ slotProps.item.status }}
|
||||
</template>
|
||||
</Timeline>
|
||||
</div>
|
||||
<DocSectionCode :code="code" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Timeline from '@/plex/timeline';
|
||||
import { ref } from 'vue';
|
||||
|
||||
const events = ref([
|
||||
{ status: 'Ordered', date: '15/10/2020 10:30', icon: 'pi pi-shopping-cart', color: '#9C27B0' },
|
||||
{ status: 'Processing', date: '15/10/2020 14:00', icon: 'pi pi-cog', color: '#673AB7' },
|
||||
{ status: 'Shipped', date: '15/10/2020 16:15', icon: 'pi pi-shopping-cart', color: '#FF9800' },
|
||||
{ status: 'Delivered', date: '16/10/2020 10:00', icon: 'pi pi-check', color: '#607D8B' }
|
||||
]);
|
||||
|
||||
const code = ref(`
|
||||
<template>
|
||||
<div class="card">
|
||||
<Timeline :value="events">
|
||||
<template #opposite="slotProps">
|
||||
<small class="text-surface-500 dark:text-surface-400">{{ slotProps.item.date }}</small>
|
||||
</template>
|
||||
<template #content="slotProps">
|
||||
{{ slotProps.item.status }}
|
||||
</template>
|
||||
</Timeline>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Timeline from '@/plex/timeline';
|
||||
import { ref } from 'vue';
|
||||
|
||||
const events = ref([
|
||||
{ status: 'Ordered', date: '15/10/2020 10:30', icon: 'pi pi-shopping-cart', color: '#9C27B0' },
|
||||
{ status: 'Processing', date: '15/10/2020 14:00', icon: 'pi pi-cog', color: '#673AB7' },
|
||||
{ status: 'Shipped', date: '15/10/2020 16:15', icon: 'pi pi-shopping-cart', color: '#FF9800' },
|
||||
{ status: 'Delivered', date: '16/10/2020 10:00', icon: 'pi pi-check', color: '#607D8B' }
|
||||
]);
|
||||
<\/script>
|
||||
`);
|
||||
</script>
|
|
@ -0,0 +1,89 @@
|
|||
<template>
|
||||
<DocSectionText v-bind="$attrs">
|
||||
<p>Sample implementation with custom content and styled markers.</p>
|
||||
</DocSectionText>
|
||||
<div class="card">
|
||||
<Timeline :value="events" align="alternate">
|
||||
<template #marker="slotProps">
|
||||
<span class="flex w-8 h-8 items-center justify-center text-white rounded-full z-10 shadow-sm" :style="{ backgroundColor: slotProps.item.color }">
|
||||
<i :class="slotProps.item.icon"></i>
|
||||
</span>
|
||||
</template>
|
||||
<template #content="slotProps">
|
||||
<Card class="mt-4">
|
||||
<template #title>
|
||||
{{ slotProps.item.status }}
|
||||
</template>
|
||||
<template #subtitle>
|
||||
{{ slotProps.item.date }}
|
||||
</template>
|
||||
<template #content>
|
||||
<img v-if="slotProps.item.image" :src="`https://primefaces.org/cdn/primevue/images/product/${slotProps.item.image}`" :alt="slotProps.item.name" width="200" class="shadow-sm" />
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Inventore sed consequuntur error repudiandae numquam deserunt quisquam repellat libero asperiores earum nam nobis, culpa ratione quam perferendis esse, cupiditate
|
||||
neque quas!
|
||||
</p>
|
||||
<Button label="Read more" text></Button>
|
||||
</template>
|
||||
</Card>
|
||||
</template>
|
||||
</Timeline>
|
||||
</div>
|
||||
<DocSectionCode :code="code" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Timeline from '@/plex/timeline';
|
||||
import { ref } from 'vue';
|
||||
|
||||
const events = ref([
|
||||
{ status: 'Ordered', date: '15/10/2020 10:30', icon: 'pi pi-shopping-cart', color: '#9C27B0', image: 'game-controller.jpg' },
|
||||
{ status: 'Processing', date: '15/10/2020 14:00', icon: 'pi pi-cog', color: '#673AB7' },
|
||||
{ status: 'Shipped', date: '15/10/2020 16:15', icon: 'pi pi-shopping-cart', color: '#FF9800' },
|
||||
{ status: 'Delivered', date: '16/10/2020 10:00', icon: 'pi pi-check', color: '#607D8B' }
|
||||
]);
|
||||
|
||||
const code = ref(`
|
||||
<template>
|
||||
<div class="card">
|
||||
<Timeline :value="events" align="alternate">
|
||||
<template #marker="slotProps">
|
||||
<span class="flex w-8 h-8 items-center justify-center text-white rounded-full z-10 shadow-sm" :style="{ backgroundColor: slotProps.item.color }">
|
||||
<i :class="slotProps.item.icon"></i>
|
||||
</span>
|
||||
</template>
|
||||
<template #content="slotProps">
|
||||
<Card class="mt-4">
|
||||
<template #title>
|
||||
{{ slotProps.item.status }}
|
||||
</template>
|
||||
<template #subtitle>
|
||||
{{ slotProps.item.date }}
|
||||
</template>
|
||||
<template #content>
|
||||
<img v-if="slotProps.item.image" :src="\`https://primefaces.org/cdn/primevue/images/product/\${slotProps.item.image}\`" :alt="slotProps.item.name" width="200" class="shadow-sm" />
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Inventore sed consequuntur error repudiandae numquam deserunt quisquam repellat libero asperiores earum nam nobis, culpa ratione quam perferendis esse, cupiditate
|
||||
neque quas!
|
||||
</p>
|
||||
<Button label="Read more" text></Button>
|
||||
</template>
|
||||
</Card>
|
||||
</template>
|
||||
</Timeline>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Timeline from '@/plex/timeline';
|
||||
import { ref } from 'vue';
|
||||
|
||||
const events = ref([
|
||||
{ status: 'Ordered', date: '15/10/2020 10:30', icon: 'pi pi-shopping-cart', color: '#9C27B0', image: 'game-controller.jpg' },
|
||||
{ status: 'Processing', date: '15/10/2020 14:00', icon: 'pi pi-cog', color: '#673AB7' },
|
||||
{ status: 'Shipped', date: '15/10/2020 16:15', icon: 'pi pi-shopping-cart', color: '#FF9800' },
|
||||
{ status: 'Delivered', date: '16/10/2020 10:00', icon: 'pi pi-check', color: '#607D8B' }
|
||||
]);
|
||||
<\/script>
|
||||
`);
|
||||
</script>
|
|
@ -0,0 +1,51 @@
|
|||
<template>
|
||||
<DocComponent title="Vue Timeline Component" header="Timeline" description="Timeline visualizes a series of chained events." :componentDocs="docs" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AlignmentDoc from '@/doc/timeline/AlignmentDoc.vue';
|
||||
import BasicDoc from '@/doc/timeline/BasicDoc.vue';
|
||||
import HorizontalDoc from '@/doc/timeline/HorizontalDoc.vue';
|
||||
import ImportDoc from '@/doc/timeline/ImportDoc.vue';
|
||||
import OppositeDoc from '@/doc/timeline/OppositeDoc.vue';
|
||||
import TemplateDoc from '@/doc/timeline/TemplateDoc.vue';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
docs: [
|
||||
{
|
||||
id: 'import',
|
||||
label: 'Import',
|
||||
component: ImportDoc
|
||||
},
|
||||
{
|
||||
id: 'basic',
|
||||
label: 'Basic',
|
||||
component: BasicDoc
|
||||
},
|
||||
{
|
||||
id: 'alignment',
|
||||
label: 'Alignment',
|
||||
component: AlignmentDoc
|
||||
},
|
||||
{
|
||||
id: 'opposite',
|
||||
label: 'Opposite',
|
||||
component: OppositeDoc
|
||||
},
|
||||
{
|
||||
id: 'template',
|
||||
label: 'Template',
|
||||
component: TemplateDoc
|
||||
},
|
||||
{
|
||||
id: 'horizontal',
|
||||
label: 'Horizontal',
|
||||
component: HorizontalDoc
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1,43 @@
|
|||
<template>
|
||||
<Timeline unstyled :pt="theme">
|
||||
<template v-for="(_, slotName) in $slots" v-slot:[slotName]="slotProps">
|
||||
<slot :name="slotName" v-bind="slotProps ?? {}" />
|
||||
</template>
|
||||
</Timeline>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Timeline from 'primevue/timeline';
|
||||
import { ref } from 'vue';
|
||||
|
||||
const theme = ref({
|
||||
root: `flex flex-col flex-grow
|
||||
p-horizontal:flex-row`,
|
||||
event: `group flex relative min-h-20 last:min-h-0
|
||||
p-right:flex-row-reverse
|
||||
p-horizontal:flex-col p-horizontal:flex-1 p-horizontal:last:flex-none
|
||||
p-bottom:flex-col-reverse
|
||||
p-vertical:p-alternate:even:flex-row-reverse
|
||||
p-horizontal:p-alternate:even:flex-col-reverse`,
|
||||
eventOpposite: `flex-1
|
||||
p-left:text-end p-right:text-start
|
||||
p-vertical:py-0 p-vertical:px-4 p-vertical:leading-none
|
||||
p-vertical:p-alternate:group-odd:text-end p-vertical:p-alternate:group-even:text-start
|
||||
p-horizontal:py-4 p-horizontal:px-0`,
|
||||
eventSeparator: `flex-none flex flex-col items-center p-horizontal:flex-row`,
|
||||
eventMarker: `inline-flex items-center justify-center relative self-baseline
|
||||
border-2 rounded-full border-surface-200 dark:border-surface-700 w-[1.125rem] h-[1.125rem]
|
||||
bg-surface-0 dark:bg-surface-900
|
||||
before:rounded-full before:w-[0.375rem] before:h-[0.375rem] before:bg-primary
|
||||
after:absolute after:w-full after:h-full after:rounded-full after:shadow-[0px_0.5px_0px_0px_rgba(0,0,0,0.06),0px_1px_1px_0px_rgba(0,0,0,0.12)]
|
||||
p-horizontal:flex-row`,
|
||||
eventConnector: `flex-grow bg-surface-200 dark:bg-surface-700
|
||||
p-vertical:w-[2px]
|
||||
p-horizontal:w-full p-horizontal:h-[2px]`,
|
||||
eventContent: `flex-1
|
||||
p-left:text-start p-right:text-end
|
||||
p-vertical:py-0 p-vertical:px-4 p-vertical:leading-none
|
||||
p-vertical:p-alternate:group-odd:text-start p-vertical:p-alternate:group-even:text-end
|
||||
p-horizontal:py-4 p-horizontal:px-0`
|
||||
});
|
||||
</script>
|
|
@ -18,6 +18,11 @@ export default {
|
|||
addVariant('p-checked', '&[data-p~="checked"]');
|
||||
addVariant('p-disabled', '&[data-p~="disabled"]');
|
||||
addVariant('p-enabled', '&:not([data-p~="disabled"])');
|
||||
addVariant('p-left', '&[data-p~="left"]');
|
||||
addVariant('p-right', '&[data-p~="right"]');
|
||||
addVariant('p-top', '&[data-p~="top"]');
|
||||
addVariant('p-bottom', '&[data-p~="bottom"]');
|
||||
addVariant('p-alternate', '&[data-p~="alternate"]');
|
||||
})
|
||||
],
|
||||
theme: {
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
<template>
|
||||
<div :class="cx('root')" v-bind="ptmi('root')">
|
||||
<div v-for="(item, index) of value" :key="getKey(item, index)" :class="cx('event')" v-bind="getPTOptions('event', index)">
|
||||
<div :class="cx('eventOpposite', { index })" v-bind="getPTOptions('eventOpposite', index)">
|
||||
<div :class="cx('root')" v-bind="ptmi('root')" :data-p="dataP">
|
||||
<div v-for="(item, index) of value" :key="getKey(item, index)" :class="cx('event')" v-bind="getPTOptions('event', index)" :data-p="dataP">
|
||||
<div :class="cx('eventOpposite', { index })" v-bind="getPTOptions('eventOpposite', index)" :data-p="dataP">
|
||||
<slot name="opposite" :item="item" :index="index"></slot>
|
||||
</div>
|
||||
<div :class="cx('eventSeparator')" v-bind="getPTOptions('eventSeparator', index)">
|
||||
<div :class="cx('eventSeparator')" v-bind="getPTOptions('eventSeparator', index)" :data-p="dataP">
|
||||
<slot name="marker" :item="item" :index="index">
|
||||
<div :class="cx('eventMarker')" v-bind="getPTOptions('eventMarker', index)"></div>
|
||||
<div :class="cx('eventMarker')" v-bind="getPTOptions('eventMarker', index)" :data-p="dataP"></div>
|
||||
</slot>
|
||||
<slot v-if="index !== value.length - 1" name="connector" :item="item" :index="index">
|
||||
<div :class="cx('eventConnector')" v-bind="getPTOptions('eventConnector', index)"></div>
|
||||
<div :class="cx('eventConnector')" v-bind="getPTOptions('eventConnector', index)" :data-p="dataP"></div>
|
||||
</slot>
|
||||
</div>
|
||||
<div :class="cx('eventContent')" v-bind="getPTOptions('eventContent', index)">
|
||||
<div :class="cx('eventContent')" v-bind="getPTOptions('eventContent', index)" :data-p="dataP">
|
||||
<slot name="content" :item="item" :index="index"></slot>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -20,6 +20,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { cn } from '@primeuix/utils';
|
||||
import { resolveFieldData } from '@primeuix/utils/object';
|
||||
import BaseTimeline from './BaseTimeline.vue';
|
||||
|
||||
|
@ -39,6 +40,14 @@ export default {
|
|||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
dataP() {
|
||||
return cn({
|
||||
[this.layout]: this.layout,
|
||||
[this.align]: this.align
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
Loading…
Reference in New Issue