Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"oxlint": "^1.11.1",
"prettier": "^3.6.2",
"tailwindcss": "^4.1.11",
"typescript": "~5.9.2",
"typescript": "~5.9.3",
"vite": "^7.1.1",
"vite-plugin-vue-devtools": "^8.0.0",
"vitest": "^3.2.4",
Expand All @@ -70,6 +70,10 @@
"vite@>=5.0.0 <5.4.16": ">=5.4.16",
"vite@>=5.0.0 <5.4.17": ">=5.4.17",
"vite@>=5.0.0 <5.4.18": ">=5.4.18"
}
},
"onlyBuiltDependencies": [
"@tailwindcss/oxide",
"esbuild"
]
}
}
230 changes: 115 additions & 115 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

53 changes: 53 additions & 0 deletions src/assets/base.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,57 @@
@import 'tailwindcss';
@import 'flowbite-vue/index.css';

@plugin "flowbite/plugin";
@source "../../node_modules/flowbite-vue";

@keyframes left-slide-in {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(0);
}
}

@keyframes left-slide-out {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-100%);
}
}

@keyframes fade-in {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}

@keyframes fade-out {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}

@utility animate-left-slide-in {
animation: left-slide-in 0.2s ease;
}

@utility animate-left-slide-out {
animation: left-slide-out 0.2s ease;
}

@utility animate-fade-in {
animation: fade-in 0.2s ease;
}

@utility animate-fade-out {
animation: fade-out 0.2s ease;
}
13 changes: 13 additions & 0 deletions src/assets/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,16 @@ body,
width: 100%;
font-family: 'Inter';
}

::-webkit-scrollbar {
width: 20px; /* Total width including `border-width` of scrollbar thumb */
height: 0;
}
::-webkit-scrollbar-thumb {
height: 1em;
border: 6px solid rgba(0, 0, 0, 0); /* Transparent border together with `background-clip: padding-box` does the trick */
background-clip: padding-box;
-webkit-border-radius: 1em;
background-color: theme('colors.gray.600');
-webkit-box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.025);
}
33 changes: 33 additions & 0 deletions src/components/Dropzone.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<script setup lang="ts">
import { ref } from 'vue'

const files = defineModel<File[]>()
const fileInputKey = ref(0)

const removeFile = (file: File) => {
files.value = files.value?.filter((f) => f !== file)
// Reset the file input to clear its internal state
fileInputKey.value++
}
</script>

<template>
<div class="flex flex-col gap-4">
<fwb-file-input :key="fileInputKey" v-model="files" dropzone multiple> </fwb-file-input>
<div v-if="files && files.length !== 0" class="p-2 flex flex-col gap-4">
<div class="flex justify-between items-center" v-for="(file, index) in files" :key="index">
<div class="flex items-center gap-2">
<Icon icon="flowbite:file-outline" width="16" height="16" class="text-gray-400" />
<span>{{ file.name }}</span>
</div>
<Icon
icon="flowbite:x-outline"
width="16"
height="16"
class="text-gray-400 cursor-pointer"
@click="removeFile(file)"
/>
</div>
</div>
</div>
</template>
22 changes: 22 additions & 0 deletions src/components/Form.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<script setup lang="ts">
const errors = defineModel<string[]>({ required: true })
</script>

<template>
<div class="flex flex-col gap-4 h-full">
<fwb-alert v-if="errors.length > 0" type="danger">
<span class="font-medium">Ensure that these requirements are met:</span>
<ul class="mt-1.5 ml-4 list-disc list-inside">
<li v-for="error in errors" :key="error">{{ error }}</li>
</ul>
</fwb-alert>

<div class="flex flex-col gap-3 basis-full overflow-y-auto">
<slot name="content" />
</div>

<div class="grid grid-cols-2 gap-3">
<slot name="footer" />
</div>
</div>
</template>
5 changes: 4 additions & 1 deletion src/components/Loader.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
<template>
<div class="flex justify-center items-center h-full w-full">
<div class="flex flex-col gap-2 justify-center items-center h-full w-full">
<fwb-spinner size="12" />
<p class="text-sm text-gray-400">
<slot name="text" />
</p>
</div>
</template>
5 changes: 5 additions & 0 deletions src/components/Separator.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<template>
<div class="p-2">
<hr class="h-px border-0 bg-gray-600" />
</div>
</template>
53 changes: 53 additions & 0 deletions src/components/SideModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<script setup lang="ts">
import { onMounted } from 'vue'

const modalShown = defineModel<boolean>()

const closeModal = () => {
modalShown.value = false
}

onMounted(() => {
window.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
closeModal()
}
})

window.addEventListener('click', (e) => {
if (e.target === document.querySelector('#modal-container')) {
closeModal()
}
})
})
</script>

<template>
<div
v-if="modalShown"
id="modal-container"
class="lg:bg-gray-600/80 absolute top-0 left-0 w-full h-full z-50"
>
<div
class="w-full lg:w-[596px] bg-gray-900 h-full p-4 flex flex-col gap-3"
:class="{ 'animate-left-slide-in': modalShown }"
>
<div class="flex justify-between items-center">
<h2 class="text-base text-gray-400 font-semibold uppercase"><slot name="header-text" /></h2>

<span @click="closeModal" class="cursor-pointer">
<Icon
icon="flowbite:x-outline"
width="24"
height="24"
class="text-gray-400 hover:text-gray-300"
/>
</span>
</div>

<div class="overflow-y-auto basis-full">
<slot name="content" />
</div>
</div>
</div>
</template>
22 changes: 18 additions & 4 deletions src/components/Sidebar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,16 @@ const logout = () => {
id="main-sidebar"
class="w-72 fixed top-0 left-0 z-40 border-solid border-r border-gray-700 transition-transform -translate-x-full sm:translate-x-0"
>
<fwb-sidebar-logo name="Squiglink Studio" logo="/src/assets/logo.svg" tag="router-link" />
<fwb-sidebar-logo
name="Squiglink Studio"
logo="/src/assets/logo.svg"
tag="router-link"
:to="{ name: 'home' }"
/>

<fwb-sidebar-item
to="/databases"
tag="router-link"
:to="{ name: 'databases' }"
:class="{ 'bg-gray-700': $route.path === '/databases' || $route.path === '/' }"
>
<template #icon>
Expand All @@ -29,15 +35,23 @@ const logout = () => {
<template #default>Databases</template>
</fwb-sidebar-item>

<fwb-sidebar-item to="/brands" :class="{ 'bg-gray-700': $route.path === '/brands' }">
<fwb-sidebar-item
tag="router-link"
:to="{ name: 'brands' }"
:class="{ 'bg-gray-700': $route.path === '/brands' }"
>
<template #icon>
<Icon icon="flowbite:grid-solid" width="24" height="24" class="text-gray-400" />
</template>

<template #default>Brands</template>
</fwb-sidebar-item>

<fwb-sidebar-item to="/models" :class="{ 'bg-gray-700': $route.path === '/models' }">
<fwb-sidebar-item
tag="router-link"
:to="{ name: 'models' }"
:class="{ 'bg-gray-700': $route.path === '/models' }"
>
<template #icon>
<Icon icon="flowbite:headphones-solid" width="24" height="24" class="text-gray-400" />
</template>
Expand Down
10 changes: 8 additions & 2 deletions src/components/Table.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ const props = defineProps<{

const tableRows = defineModel<object[]>()
const currentPage = defineModel<number>('currentPage', { required: true })
const searchQuery = defineModel<string>('searchQuery')
const loading = defineModel<boolean>('loading', { required: true })
const selectedRows = ref([])
</script>
Expand All @@ -31,7 +30,14 @@ const selectedRows = ref([])
</template>
</TableActions>

<Loader v-if="loading" />
<div class="p-4 bg-gray-800 rounded-b-lg" v-if="loading">
<Loader>
<template #text>
<slot name="text">Fetching data...</slot>
</template>
</Loader>
</div>

<div v-else>
<fwb-table hoverable class="rounded-lg !rounded-t-none">
<fwb-table-head>
Expand Down
32 changes: 21 additions & 11 deletions src/layouts/MainLayout.vue
Original file line number Diff line number Diff line change
@@ -1,27 +1,37 @@
<script setup lang="ts">
import Sidebar from '@/components/Sidebar.vue'
import { initFlowbite } from 'flowbite';
import { onMounted } from 'vue';
import { initFlowbite } from 'flowbite'
import { onMounted } from 'vue'

onMounted(() => {
initFlowbite();
});
initFlowbite()
})
</script>

<template>
<div class="dark bg-gray-900">
<div class="dark dark:text-white bg-gray-900">
<Sidebar />

<div class="sm:ml-72 h-screen overflow-x-auto p-4 lg:p-6 flex flex-col gap-4">
<div class="flex gap-4 items-center content-center">
<fwb-button color="alternative" data-drawer-target="main-sidebar"
data-drawer-toggle="main-sidebar" aria-controls="main-sidebar"
class="inline-flex items-center p-2 rounded-lg sm:hidden">
<fwb-button
color="alternative"
data-drawer-target="main-sidebar"
data-drawer-toggle="main-sidebar"
aria-controls="main-sidebar"
class="inline-flex items-center p-2 rounded-lg sm:hidden"
>
<Icon icon="flowbite:bars-from-left-outline" width="24" height="24" />
</fwb-button>
<h1 class="text-3xl text-white">
<slot name="header-text" />
</h1>
<div class="w-full flex items-center justify-between">
<h1 class="text-3xl text-white flex-3">
<slot name="header-text" />
</h1>

<div class="w-full grid grid-cols-2 gap-3 flex-1">
<slot name="header-buttons" />
</div>
</div>
</div>

<slot name="content" />
Expand Down
12 changes: 11 additions & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import {
FwbButton,
FwbButtonGroup,
FwbCard,
FwbFileInput,
FwbHeading,
FwbInput,
FwbPagination,
FwbSelect,
Expand Down Expand Up @@ -41,6 +43,8 @@ const components = {
FwbButton,
FwbButtonGroup,
FwbCard,
FwbFileInput,
FwbHeading,
FwbInput,
FwbPagination,
FwbSelect,
Expand All @@ -66,7 +70,13 @@ app.use(createPinia())
app.use(router)
app.use(Vue3Toastify, {
autoClose: 3000,
// ...
position: 'top-center',
theme: 'dark',
multiple: false,
transition: 'slide',
clearOnUrlChange: false,
pauseOnHover: true,
pauseOnFocusLoss: false,
} as ToastContainerOptions)

app.mount('#app')
Loading