DONE: layout letter with letterhead and footer, notification for newly...
This commit is contained in:
@@ -126,361 +126,4 @@ import { Box } from "@/components/ui/box";
|
||||
</template>
|
||||
</Preview>
|
||||
</div>
|
||||
<div id="installation">
|
||||
<SectionTitle>Installation</SectionTitle>
|
||||
<SectionContent>Install the following dependencies:</SectionContent>
|
||||
<InstallPackage>add @zag-js/vue @zag-js/accordion</InstallPackage>
|
||||
<SectionContent>
|
||||
Copy and paste the following code into your project.
|
||||
</SectionContent>
|
||||
<PreviewCode title="components/ui/accordion/AccordionContent.vue">
|
||||
{{ `
|
||||
<script lang="ts" setup>
|
||||
import { accordionContent } from "@mykopkb/core/styles/accordion.styles";
|
||||
import { cn } from "@mykopkb/core/utils/cn";
|
||||
import { inject } from "vue";
|
||||
import { Slot } from "@/components/ui/slot";
|
||||
import type { Api, ItemProps } from "@zag-js/accordion";
|
||||
|
||||
const {
|
||||
class: className,
|
||||
asChild = false,
|
||||
...props
|
||||
} = defineProps<{
|
||||
class?: string;
|
||||
asChild?: boolean;
|
||||
}>();
|
||||
|
||||
const api = inject<Api>("accordionApi");
|
||||
const item = inject<ItemProps>("accordionItem");
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Slot :class="cn([className, accordionContent])"
|
||||
v-bind="{ ...props, ...$attrs, ...api?.getItemContentProps(item!) }">
|
||||
<slot v-if="asChild" />
|
||||
<div v-else>
|
||||
<slot />
|
||||
</div>
|
||||
</Slot>
|
||||
</template>
|
||||
` }}
|
||||
</PreviewCode>
|
||||
<PreviewCode title="components/ui/accordion/AccordionItem.vue">
|
||||
{{ `
|
||||
<script lang="ts" setup>
|
||||
import { accordionItemVariants } from "@mykopkb/core/styles/accordion.styles";
|
||||
import {
|
||||
boxVariants,
|
||||
type BoxVariants,
|
||||
} from "@mykopkb/core/styles/box.styles";
|
||||
import { cn } from "@mykopkb/core/utils/cn";
|
||||
import { inject, provide } from "vue";
|
||||
import { Slot } from "@/components/ui/slot";
|
||||
import type { Api, ItemProps } from "@zag-js/accordion";
|
||||
|
||||
const {
|
||||
raised,
|
||||
class: className,
|
||||
asChild = false,
|
||||
...props
|
||||
} = defineProps<
|
||||
BoxVariants & ItemProps & { class?: string; asChild?: boolean }
|
||||
>();
|
||||
|
||||
const variant = inject<"default" | "boxed">("accordionVariant", "default");
|
||||
const api = inject<Api>("accordionApi");
|
||||
|
||||
provide("accordionItem", props);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Slot :class="
|
||||
cn([
|
||||
className,
|
||||
variant == 'boxed' ? boxVariants({ raised, className }) : '',
|
||||
accordionItemVariants({ variant, className }),
|
||||
])
|
||||
" v-bind="{ ...props, ...$attrs, ...api?.getItemProps(props) }">
|
||||
<slot v-if="asChild" />
|
||||
<div v-else>
|
||||
<slot />
|
||||
</div>
|
||||
</Slot>
|
||||
</template>
|
||||
` }}
|
||||
</PreviewCode>
|
||||
<PreviewCode title="components/ui/accordion/AccordionRoot.vue">
|
||||
{{`
|
||||
<script lang="ts" setup>
|
||||
import { accordionRootVariants } from "@mykopkb/core/styles/accordion.styles";
|
||||
import { cn } from "@mykopkb/core/utils/cn";
|
||||
import { provide, computed } from "vue";
|
||||
import * as accordion from "@zag-js/accordion";
|
||||
import type { Props } from "@zag-js/accordion";
|
||||
import { useMachine, normalizeProps } from "@zag-js/vue";
|
||||
import { Slot } from "@/components/ui/slot";
|
||||
|
||||
const {
|
||||
class: className,
|
||||
variant = "default",
|
||||
asChild = false,
|
||||
collapsible = true,
|
||||
...props
|
||||
} = defineProps<
|
||||
Partial<Props> & {
|
||||
class?: string;
|
||||
variant?: "default" | "boxed";
|
||||
asChild?: boolean;
|
||||
}
|
||||
>();
|
||||
|
||||
const service = useMachine(accordion.machine, {
|
||||
collapsible,
|
||||
...props,
|
||||
id: crypto.randomUUID(),
|
||||
});
|
||||
|
||||
const api = computed(() => accordion.connect(service, normalizeProps));
|
||||
|
||||
provide("accordionVariant", variant);
|
||||
provide("accordionApi", api);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Slot :class="cn([className, accordionRootVariants({ variant, className })])"
|
||||
v-bind="{ ...props, ...$attrs, ...api.getRootProps() }">
|
||||
<slot v-if="asChild" />
|
||||
<div v-else>
|
||||
<slot />
|
||||
</div>
|
||||
</Slot>
|
||||
</template>
|
||||
` }}
|
||||
</PreviewCode>
|
||||
<PreviewCode title="components/ui/accordion/AccordionTrigger.vue">
|
||||
{{ `
|
||||
<script lang="ts" setup>
|
||||
import { ChevronDownIcon } from "@lucide/vue";
|
||||
import {
|
||||
accordionTrigger,
|
||||
accordionItemIndicator,
|
||||
} from "@mykopkb/core/styles/accordion.styles";
|
||||
import { cn } from "@mykopkb/core/utils/cn";
|
||||
import { inject } from "vue";
|
||||
import { Slot } from "@/components/ui/slot";
|
||||
import type { Api, ItemProps } from "@zag-js/accordion";
|
||||
|
||||
const {
|
||||
class: className,
|
||||
asChild = false,
|
||||
...props
|
||||
} = defineProps<{
|
||||
class?: string;
|
||||
asChild?: boolean;
|
||||
}>();
|
||||
|
||||
const api = inject<Api>("accordionApi");
|
||||
const item = inject<ItemProps>("accordionItem");
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Slot :class="cn([className, accordionTrigger])"
|
||||
v-bind="{ ...props, ...$attrs, ...api?.getItemTriggerProps(item!) }">
|
||||
<slot v-if="asChild" />
|
||||
<button v-else>
|
||||
<slot />
|
||||
<div v-bind="api?.getItemIndicatorProps(item!)" :class="cn(accordionItemIndicator)">
|
||||
<ChevronDownIcon />
|
||||
</div>
|
||||
</button>
|
||||
</Slot>
|
||||
</template>
|
||||
` }}
|
||||
</PreviewCode>
|
||||
<PreviewCode title="components/ui/accordion/index.ts">
|
||||
{{ `
|
||||
export { default as AccordionRoot } from "./AccordionRoot.vue";
|
||||
export { default as AccordionItem } from "./AccordionItem.vue";
|
||||
export { default as AccordionTrigger } from "./AccordionTrigger.vue";
|
||||
export { default as AccordionContent } from "./AccordionContent.vue";
|
||||
` }}
|
||||
</PreviewCode>
|
||||
<SectionContent>
|
||||
Update the import paths to match your project setup.
|
||||
</SectionContent>
|
||||
</div>
|
||||
<div id="usage">
|
||||
<SectionTitle>Usage</SectionTitle>
|
||||
<PreviewCode>
|
||||
{{ `
|
||||
import {
|
||||
AccordionRoot,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
AccordionContent,
|
||||
} from "@/components/ui/accordion";
|
||||
import { Box } from "@/components/ui/box";
|
||||
` }}
|
||||
</PreviewCode>
|
||||
<PreviewCode>
|
||||
{{ `
|
||||
<Box raised="single" class="w-full">
|
||||
<AccordionRoot class="w-full" :default-value="['product-information']">
|
||||
<AccordionItem value="product-information">
|
||||
<AccordionTrigger>Product Information</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<p class="mb-2">
|
||||
Our flagship product combines cutting-edge technology with
|
||||
sleek design. Built with premium materials, it offers
|
||||
unparalleled performance and reliability.
|
||||
</p>
|
||||
<p>
|
||||
Key features include advanced processing capabilities, and an
|
||||
intuitive user interface designed for both beginners and
|
||||
experts.
|
||||
</p>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
<AccordionItem value="shipping-details">
|
||||
<AccordionTrigger>Shipping Details</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<p class="mb-2">
|
||||
We offer worldwide shipping through trusted courier partners.
|
||||
Standard delivery takes 3-5 business days, while express
|
||||
shipping ensures delivery within 1-2 business days.
|
||||
</p>
|
||||
<p>
|
||||
All orders are carefully packaged and fully insured. Track
|
||||
your shipment in real-time through our dedicated tracking
|
||||
portal.
|
||||
</p>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
<AccordionItem value="return-policy">
|
||||
<AccordionTrigger>Return Policy</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<p class="mb-2">
|
||||
We stand behind our products with a comprehensive 30-day
|
||||
return policy. If you're not completely satisfied, simply
|
||||
return the item in its original condition.
|
||||
</p>
|
||||
<p>
|
||||
Our hassle-free return process includes free return shipping
|
||||
and full refunds processed within 48 hours of receiving the
|
||||
returned item.
|
||||
</p>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</AccordionRoot>
|
||||
</Box>
|
||||
` }}
|
||||
</PreviewCode>
|
||||
</div>
|
||||
<div id="variants">
|
||||
<SectionTitle>Variants</SectionTitle>
|
||||
<SectionContent>A collection of components you can use.</SectionContent>
|
||||
<Preview>
|
||||
<template #preview>
|
||||
<AccordionRoot class="w-full" variant="boxed" :default-value="['product-information']">
|
||||
<AccordionItem raised="single" value="product-information">
|
||||
<AccordionTrigger>Product Information</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<p class="mb-2">
|
||||
Our flagship product combines cutting-edge technology with sleek
|
||||
design. Built with premium materials, it offers unparalleled
|
||||
performance and reliability.
|
||||
</p>
|
||||
<p>
|
||||
Key features include advanced processing capabilities, and an
|
||||
intuitive user interface designed for both beginners and
|
||||
experts.
|
||||
</p>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
<AccordionItem raised="single" value="shipping-details">
|
||||
<AccordionTrigger>Shipping Details</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<p class="mb-2">
|
||||
We offer worldwide shipping through trusted courier partners.
|
||||
Standard delivery takes 3-5 business days, while express
|
||||
shipping ensures delivery within 1-2 business days.
|
||||
</p>
|
||||
<p>
|
||||
All orders are carefully packaged and fully insured. Track your
|
||||
shipment in real-time through our dedicated tracking portal.
|
||||
</p>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
<AccordionItem raised="single" value="return-policy">
|
||||
<AccordionTrigger>Return Policy</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<p class="mb-2">
|
||||
We stand behind our products with a comprehensive 30-day return
|
||||
policy. If you're not completely satisfied, simply return the
|
||||
item in its original condition.
|
||||
</p>
|
||||
<p>
|
||||
Our hassle-free return process includes free return shipping and
|
||||
full refunds processed within 48 hours of receiving the returned
|
||||
item.
|
||||
</p>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</AccordionRoot>
|
||||
</template>
|
||||
<template #code>
|
||||
<PreviewCode>
|
||||
{{ `
|
||||
<AccordionRoot class="w-full" variant="boxed" :default-value="['product-information']">
|
||||
<AccordionItem raised="single" value="product-information">
|
||||
<AccordionTrigger>Product Information</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<p class="mb-2">
|
||||
Our flagship product combines cutting-edge technology with sleek
|
||||
design. Built with premium materials, it offers unparalleled
|
||||
performance and reliability.
|
||||
</p>
|
||||
<p>
|
||||
Key features include advanced processing capabilities, and an
|
||||
intuitive user interface designed for both beginners and
|
||||
experts.
|
||||
</p>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
<AccordionItem raised="single" value="shipping-details">
|
||||
<AccordionTrigger>Shipping Details</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<p class="mb-2">
|
||||
We offer worldwide shipping through trusted courier partners.
|
||||
Standard delivery takes 3-5 business days, while express
|
||||
shipping ensures delivery within 1-2 business days.
|
||||
</p>
|
||||
<p>
|
||||
All orders are carefully packaged and fully insured. Track your
|
||||
shipment in real-time through our dedicated tracking portal.
|
||||
</p>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
<AccordionItem raised="single" value="return-policy">
|
||||
<AccordionTrigger>Return Policy</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<p class="mb-2">
|
||||
We stand behind our products with a comprehensive 30-day return
|
||||
policy. If you're not completely satisfied, simply return the
|
||||
item in its original condition.
|
||||
</p>
|
||||
<p>
|
||||
Our hassle-free return process includes free return shipping and
|
||||
full refunds processed within 48 hours of receiving the returned
|
||||
item.
|
||||
</p>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</AccordionRoot>
|
||||
` }}
|
||||
</PreviewCode>
|
||||
</template>
|
||||
</Preview>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,197 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref, watch } from "vue";
|
||||
import debounce from "lodash/debounce";
|
||||
import axios from "axios";
|
||||
import {
|
||||
SectionTitle,
|
||||
SectionContent,
|
||||
InstallPackage,
|
||||
} from "@/components/docs";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import DataTable from "@/components/ui/usage/DataTable.vue";
|
||||
import type { SortConfig, TableHeader } from "@/components/ui/usage/DataTable.vue";
|
||||
import { api } from "@/core/services/api";
|
||||
import { useApiPagination } from "@/composables/useApiPagination";
|
||||
import type { PaginatedApiResponse } from "@/core/types/api";
|
||||
|
||||
type UserRole = {
|
||||
id: string;
|
||||
name: string;
|
||||
guard_name: string;
|
||||
};
|
||||
|
||||
type UserListItem = {
|
||||
id: string;
|
||||
name: string;
|
||||
email: string;
|
||||
ic_number: string;
|
||||
position: string;
|
||||
phone_number: string;
|
||||
image_url: string | null;
|
||||
status: string;
|
||||
roles: UserRole[];
|
||||
};
|
||||
|
||||
const users = ref<UserListItem[]>([]);
|
||||
const loading = ref(false);
|
||||
const error = ref<string | null>(null);
|
||||
const search = ref("");
|
||||
const statusFilter = ref("");
|
||||
const sortBy = ref<SortConfig[]>([{ key: "name", order: "asc" }]);
|
||||
const page = ref(1);
|
||||
const itemsPerPage = ref(10);
|
||||
|
||||
const { pagination, applyPagination } = useApiPagination({ per_page: 10 });
|
||||
|
||||
const headers: TableHeader[] = [
|
||||
{ title: "Name", key: "name", sortable: true },
|
||||
{ title: "Email", key: "email", sortable: true },
|
||||
{ title: "Position", key: "position", sortable: true },
|
||||
{
|
||||
title: "Role",
|
||||
key: "roles",
|
||||
sortable: false,
|
||||
exportValue: (item) =>
|
||||
item.roles?.map((role: UserRole) => role.name).join(", ") || "-",
|
||||
},
|
||||
{ title: "Status", key: "status", sortable: true },
|
||||
];
|
||||
|
||||
function statusBadgeVariant(status: string) {
|
||||
if (status === "active") return "success";
|
||||
if (status === "inactive") return "danger";
|
||||
return "pending";
|
||||
}
|
||||
|
||||
async function fetchUsers(requestPage = page.value) {
|
||||
loading.value = true;
|
||||
error.value = null;
|
||||
|
||||
try {
|
||||
const activeSort = sortBy.value[0];
|
||||
const { data } = await api.get<PaginatedApiResponse<UserListItem>>(
|
||||
"/v1/users",
|
||||
{
|
||||
params: {
|
||||
page: requestPage,
|
||||
per_page: itemsPerPage.value,
|
||||
sort_by: activeSort?.key ?? "name",
|
||||
sort_order: activeSort?.order ?? "asc",
|
||||
...(search.value.trim() ? { search: search.value.trim() } : {}),
|
||||
...(statusFilter.value.trim()
|
||||
? { status: statusFilter.value.trim() }
|
||||
: {}),
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (!data.success) {
|
||||
throw new Error(data.message ?? "Failed to load users");
|
||||
}
|
||||
|
||||
users.value = data.data;
|
||||
applyPagination(data.pagination);
|
||||
page.value = data.pagination.current_page;
|
||||
} catch (err) {
|
||||
users.value = [];
|
||||
|
||||
if (axios.isAxiosError(err)) {
|
||||
const message = (err.response?.data as { message?: string } | undefined)
|
||||
?.message;
|
||||
error.value = message ?? err.message;
|
||||
} else if (err instanceof Error) {
|
||||
error.value = err.message;
|
||||
} else {
|
||||
error.value = "Failed to load users";
|
||||
}
|
||||
|
||||
applyPagination({
|
||||
current_page: 1,
|
||||
per_page: itemsPerPage.value,
|
||||
total: 0,
|
||||
last_page: 1,
|
||||
from: null,
|
||||
to: null,
|
||||
has_more_pages: false,
|
||||
});
|
||||
page.value = 1;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleSortUpdate(value: SortConfig[]) {
|
||||
sortBy.value = value;
|
||||
fetchUsers(1);
|
||||
}
|
||||
|
||||
const debouncedSearch = debounce(() => {
|
||||
fetchUsers(1);
|
||||
}, 400);
|
||||
|
||||
watch(search, () => {
|
||||
debouncedSearch();
|
||||
});
|
||||
|
||||
watch(statusFilter, () => {
|
||||
fetchUsers(1);
|
||||
});
|
||||
|
||||
watch(page, (nextPage, previousPage) => {
|
||||
if (nextPage !== previousPage) {
|
||||
fetchUsers(nextPage);
|
||||
}
|
||||
});
|
||||
|
||||
watch(itemsPerPage, (nextValue, previousValue) => {
|
||||
if (nextValue !== previousValue) {
|
||||
fetchUsers(1);
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
fetchUsers(1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="preview" class="-mt-20 w-full space-y-4">
|
||||
<div class="flex flex-wrap items-center gap-3">
|
||||
<Input v-model="search" placeholder="Search name, email, IC, phone, role..." class="max-w-96" />
|
||||
<Input v-model="statusFilter" placeholder="Filter status (e.g. active)" class="max-w-56" />
|
||||
</div>
|
||||
|
||||
<p v-if="error" class="text-sm text-danger">{{ error }}</p>
|
||||
|
||||
<DataTable :headers="headers" :items="users" :loading="loading" :pagination="pagination" :current-sort="sortBy"
|
||||
show-pagination exportable export-file-name="users" v-model:page="page" v-model:items-per-page="itemsPerPage"
|
||||
@update:sort-by="handleSortUpdate">
|
||||
<template #item.name="{ item }">
|
||||
<span class="font-medium">{{ item.name }}</span>
|
||||
</template>
|
||||
|
||||
<template #item.email="{ item }">
|
||||
<span class="lowercase">{{ item.email }}</span>
|
||||
</template>
|
||||
|
||||
<template #item.roles="{ item }">
|
||||
{{item.roles?.map((role: UserRole) => role.name).join(", ") || "-"}}
|
||||
</template>
|
||||
|
||||
<template #item.status="{ item }">
|
||||
<Badge :variant="statusBadgeVariant(item.status)" class="capitalize">
|
||||
{{ item.status }}
|
||||
</Badge>
|
||||
</template>
|
||||
</DataTable>
|
||||
</div>
|
||||
|
||||
<div id="installation">
|
||||
<SectionTitle>Installation</SectionTitle>
|
||||
<SectionContent>Install the following dependencies:</SectionContent>
|
||||
<InstallPackage class="mb-0">
|
||||
add xlsx jspdf jspdf-autotable file-saver
|
||||
</InstallPackage>
|
||||
</div>
|
||||
</template>
|
||||
@@ -100,7 +100,7 @@ import {
|
||||
<DatePickerTableCell v-for="(month, id) in months" :key="id" :value="month.value">
|
||||
<DatePickerTableCellTrigger>{{
|
||||
month.label
|
||||
}}</DatePickerTableCellTrigger>
|
||||
}}</DatePickerTableCellTrigger>
|
||||
</DatePickerTableCell>
|
||||
</DatePickerTableRow>
|
||||
</DatePickerTableBody>
|
||||
@@ -124,7 +124,7 @@ import {
|
||||
<DatePickerTableCell v-for="(year, id) in years" :key="id" :value="year.value">
|
||||
<DatePickerTableCellTrigger>{{
|
||||
year.label
|
||||
}}</DatePickerTableCellTrigger>
|
||||
}}</DatePickerTableCellTrigger>
|
||||
</DatePickerTableCell>
|
||||
</DatePickerTableRow>
|
||||
</DatePickerTableBody>
|
||||
@@ -522,10 +522,7 @@ const api = inject<Api>("datepickerApi");
|
||||
{{ `
|
||||
<script lang="ts" setup>
|
||||
import { cn } from "@mykopkb/core/utils/cn";
|
||||
import {
|
||||
NativeSelect,
|
||||
NativeSelectOption,
|
||||
} from "@/components/ui/native-select";
|
||||
|
||||
import { datePickerYearSelect } from "@mykopkb/core/styles/datepicker.styles";
|
||||
import type { Api } from "@zag-js/date-picker";
|
||||
import { inject } from "vue";
|
||||
@@ -557,10 +554,7 @@ const api = inject<Api>("datepickerApi");
|
||||
<script lang="ts" setup>
|
||||
import { cn } from "@mykopkb/core/utils/cn";
|
||||
import { datePickerMonthSelect } from "@mykopkb/core/styles/datepicker.styles";
|
||||
import {
|
||||
NativeSelect,
|
||||
NativeSelectOption,
|
||||
} from "@/components/ui/native-select";
|
||||
|
||||
import type { Api } from "@zag-js/date-picker";
|
||||
import { inject } from "vue";
|
||||
|
||||
@@ -1312,7 +1306,7 @@ const api = inject<Api>("datepickerApi");
|
||||
<DatePickerTableCell v-for="(month, id) in months" :key="id" :value="month.value">
|
||||
<DatePickerTableCellTrigger>{{
|
||||
month.label
|
||||
}}</DatePickerTableCellTrigger>
|
||||
}}</DatePickerTableCellTrigger>
|
||||
</DatePickerTableCell>
|
||||
</DatePickerTableRow>
|
||||
</DatePickerTableBody>
|
||||
@@ -1336,7 +1330,7 @@ const api = inject<Api>("datepickerApi");
|
||||
<DatePickerTableCell v-for="(year, id) in years" :key="id" :value="year.value">
|
||||
<DatePickerTableCellTrigger>{{
|
||||
year.label
|
||||
}}</DatePickerTableCellTrigger>
|
||||
}}</DatePickerTableCellTrigger>
|
||||
</DatePickerTableCell>
|
||||
</DatePickerTableRow>
|
||||
</DatePickerTableBody>
|
||||
@@ -1649,7 +1643,7 @@ const api = inject<Api>("datepickerApi");
|
||||
<DatePickerTableCell v-for="(month, id) in months" :key="id" :value="month.value">
|
||||
<DatePickerTableCellTrigger>{{
|
||||
month.label
|
||||
}}</DatePickerTableCellTrigger>
|
||||
}}</DatePickerTableCellTrigger>
|
||||
</DatePickerTableCell>
|
||||
</DatePickerTableRow>
|
||||
</DatePickerTableBody>
|
||||
@@ -1673,7 +1667,7 @@ const api = inject<Api>("datepickerApi");
|
||||
<DatePickerTableCell v-for="(year, id) in years" :key="id" :value="year.value">
|
||||
<DatePickerTableCellTrigger>{{
|
||||
year.label
|
||||
}}</DatePickerTableCellTrigger>
|
||||
}}</DatePickerTableCellTrigger>
|
||||
</DatePickerTableCell>
|
||||
</DatePickerTableRow>
|
||||
</DatePickerTableBody>
|
||||
|
||||
@@ -1,210 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
NativeSelect,
|
||||
NativeSelectOption,
|
||||
NativeSelectOptionGroup,
|
||||
} from "@/components/ui/native-select";
|
||||
import {
|
||||
Preview,
|
||||
SectionTitle,
|
||||
SectionContent,
|
||||
PreviewCode,
|
||||
} from "@/components/docs";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="preview" class="-mt-20">
|
||||
<Preview>
|
||||
<template #preview>
|
||||
<NativeSelect class="w-56">
|
||||
<NativeSelectOption value="">Select status</NativeSelectOption>
|
||||
<NativeSelectOption value="todo">Todo</NativeSelectOption>
|
||||
<NativeSelectOption value="in-progress">In Progress</NativeSelectOption>
|
||||
<NativeSelectOption value="done">Done</NativeSelectOption>
|
||||
<NativeSelectOption value="cancelled">Cancelled</NativeSelectOption>
|
||||
</NativeSelect>
|
||||
</template>
|
||||
<template #code>
|
||||
<PreviewCode>
|
||||
{{ `
|
||||
<NativeSelect class="w-56">
|
||||
<NativeSelectOption value="">Select status</NativeSelectOption>
|
||||
<NativeSelectOption value="todo">Todo</NativeSelectOption>
|
||||
<NativeSelectOption value="in-progress">In Progress</NativeSelectOption>
|
||||
<NativeSelectOption value="done">Done</NativeSelectOption>
|
||||
<NativeSelectOption value="cancelled">Cancelled</NativeSelectOption>
|
||||
</NativeSelect>
|
||||
` }}
|
||||
</PreviewCode>
|
||||
</template>
|
||||
</Preview>
|
||||
</div>
|
||||
<div id="installation">
|
||||
<SectionTitle>Installation</SectionTitle>
|
||||
<SectionContent>
|
||||
Copy and paste the following code into your project.
|
||||
</SectionContent>
|
||||
<PreviewCode title="components/ui/native-select/NativeSelect.vue">
|
||||
{{ `
|
||||
<script lang="ts" setup>
|
||||
import { cn } from "@mykopkb/core/utils/cn";
|
||||
import { input } from "@mykopkb/core/styles/input.styles";
|
||||
import { nativeSelect } from "@mykopkb/core/styles/native-select.styles";
|
||||
|
||||
const { class: className, ...props } = defineProps<{
|
||||
class?: string;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<select :class="cn(input, nativeSelect, className)" v-bind="{ ...props, ...$attrs }">
|
||||
<slot />
|
||||
</select>
|
||||
</template>
|
||||
` }}
|
||||
</PreviewCode>
|
||||
<PreviewCode title="components/ui/native-select/NativeSelectOption.vue">
|
||||
{{ `
|
||||
<script lang="ts" setup>
|
||||
import { cn } from "@mykopkb/core/utils/cn";
|
||||
import { nativeSelectOption } from "@mykopkb/core/styles/native-select.styles";
|
||||
|
||||
const { class: className, ...props } = defineProps<{
|
||||
class?: string;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<option :class="cn(nativeSelectOption, className)" v-bind="{ ...props, ...$attrs }">
|
||||
<slot />
|
||||
</option>
|
||||
</template>
|
||||
` }}
|
||||
</PreviewCode>
|
||||
<PreviewCode title="components/ui/native-select/NativeSelectOptionGroup.vue">
|
||||
{{ `
|
||||
<script lang="ts" setup>
|
||||
import { cn } from "@mykopkb/core/utils/cn";
|
||||
import { NativeSelectOptGroup } from "@mykopkb/core/styles/native-select.styles";
|
||||
|
||||
const { class: className, ...props } = defineProps<{
|
||||
class?: string;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<optgroup :class="cn(NativeSelectOptGroup, className)" v-bind="{ ...props, ...$attrs }">
|
||||
<slot />
|
||||
</optgroup>
|
||||
</template>
|
||||
` }}
|
||||
</PreviewCode>
|
||||
<PreviewCode title="components/ui/native-select/index.ts">
|
||||
{{ `
|
||||
export { default as NativeSelect } from "./NativeSelect.vue";
|
||||
export { default as NativeSelectOption } from "./NativeSelectOption.vue";
|
||||
export { default as NativeSelectOptionGroup } from "./NativeSelectOptionGroup.vue";
|
||||
` }}
|
||||
</PreviewCode>
|
||||
<SectionContent>
|
||||
Update the import paths to match your project setup.
|
||||
</SectionContent>
|
||||
</div>
|
||||
<div id="usage">
|
||||
<SectionTitle>Usage</SectionTitle>
|
||||
<PreviewCode>
|
||||
{{ `
|
||||
import {
|
||||
NativeSelect,
|
||||
NativeSelectOption,
|
||||
NativeSelectOptionGroup,
|
||||
} from "@/components/ui/native-select";
|
||||
` }}
|
||||
</PreviewCode>
|
||||
<PreviewCode>
|
||||
{{ `
|
||||
<NativeSelect class="w-56">
|
||||
<NativeSelectOption value="">Select status</NativeSelectOption>
|
||||
<NativeSelectOption value="todo">Todo</NativeSelectOption>
|
||||
<NativeSelectOption value="in-progress">In Progress</NativeSelectOption>
|
||||
<NativeSelectOption value="done">Done</NativeSelectOption>
|
||||
<NativeSelectOption value="cancelled">Cancelled</NativeSelectOption>
|
||||
</NativeSelect>
|
||||
` }}
|
||||
</PreviewCode>
|
||||
</div>
|
||||
<div id="variants">
|
||||
<SectionTitle>Variants</SectionTitle>
|
||||
<SectionContent>A collection of components you can use.</SectionContent>
|
||||
<Preview>
|
||||
<template #preview>
|
||||
<NativeSelect class="w-56">
|
||||
<NativeSelectOption value="">Select department</NativeSelectOption>
|
||||
<NativeSelectOptionGroup label="Engineering">
|
||||
<NativeSelectOption value="frontend">Frontend</NativeSelectOption>
|
||||
<NativeSelectOption value="backend">Backend</NativeSelectOption>
|
||||
<NativeSelectOption value="devops">DevOps</NativeSelectOption>
|
||||
</NativeSelectOptionGroup>
|
||||
<NativeSelectOptionGroup label="Sales">
|
||||
<NativeSelectOption value="sales-rep">Sales Rep</NativeSelectOption>
|
||||
<NativeSelectOption value="account-manager">Account Manager</NativeSelectOption>
|
||||
<NativeSelectOption value="sales-director">Sales Director</NativeSelectOption>
|
||||
</NativeSelectOptionGroup>
|
||||
<NativeSelectOptionGroup label="Operations">
|
||||
<NativeSelectOption value="support">Customer Support</NativeSelectOption>
|
||||
<NativeSelectOption value="product-manager">Product Manager</NativeSelectOption>
|
||||
<NativeSelectOption value="ops-manager">Operations Manager</NativeSelectOption>
|
||||
</NativeSelectOptionGroup>
|
||||
</NativeSelect>
|
||||
</template>
|
||||
<template #code>
|
||||
<PreviewCode>
|
||||
{{ `
|
||||
<NativeSelect class="w-56">
|
||||
<NativeSelectOption value="">Select department</NativeSelectOption>
|
||||
<NativeSelectOptionGroup label="Engineering">
|
||||
<NativeSelectOption value="frontend">Frontend</NativeSelectOption>
|
||||
<NativeSelectOption value="backend">Backend</NativeSelectOption>
|
||||
<NativeSelectOption value="devops">DevOps</NativeSelectOption>
|
||||
</NativeSelectOptionGroup>
|
||||
<NativeSelectOptionGroup label="Sales">
|
||||
<NativeSelectOption value="sales-rep">Sales Rep</NativeSelectOption>
|
||||
<NativeSelectOption value="account-manager">Account Manager</NativeSelectOption>
|
||||
<NativeSelectOption value="sales-director">Sales Director</NativeSelectOption>
|
||||
</NativeSelectOptionGroup>
|
||||
<NativeSelectOptionGroup label="Operations">
|
||||
<NativeSelectOption value="support">Customer Support</NativeSelectOption>
|
||||
<NativeSelectOption value="product-manager">Product Manager</NativeSelectOption>
|
||||
<NativeSelectOption value="ops-manager">Operations Manager</NativeSelectOption>
|
||||
</NativeSelectOptionGroup>
|
||||
</NativeSelect>
|
||||
` }}
|
||||
</PreviewCode>
|
||||
</template>
|
||||
</Preview>
|
||||
<Preview>
|
||||
<template #preview>
|
||||
<NativeSelect class="w-56" disabled>
|
||||
<NativeSelectOption value="">Select status</NativeSelectOption>
|
||||
<NativeSelectOption value="todo">Todo</NativeSelectOption>
|
||||
<NativeSelectOption value="in-progress">In Progress</NativeSelectOption>
|
||||
<NativeSelectOption value="done">Done</NativeSelectOption>
|
||||
<NativeSelectOption value="cancelled">Cancelled</NativeSelectOption>
|
||||
</NativeSelect>
|
||||
</template>
|
||||
<template #code>
|
||||
<PreviewCode>
|
||||
{{ `
|
||||
<NativeSelect class="w-56" disabled>
|
||||
<NativeSelectOption value="">Select status</NativeSelectOption>
|
||||
<NativeSelectOption value="todo">Todo</NativeSelectOption>
|
||||
<NativeSelectOption value="in-progress">In Progress</NativeSelectOption>
|
||||
<NativeSelectOption value="done">Done</NativeSelectOption>
|
||||
<NativeSelectOption value="cancelled">Cancelled</NativeSelectOption>
|
||||
</NativeSelect>
|
||||
` }}
|
||||
</PreviewCode>
|
||||
</template>
|
||||
</Preview>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user