Getting Started

This guide walks you through the "TaskRecorder" application, a practical example of how to build with Katlux.

The TaskRecorder Application

The TaskRecorder is a basic application that allows users to pick a date, add tasks with titles and descriptions, and visualize them on a calendar. It demonstrates the seamless integration of form elements, layout components, and data visualization.

Download & Run

You can clone the official repository and run it locally to see it in action:

Bash
git clone git@github.com:katlux-ui/TaskRecorder.git
cd TaskRecorder
npm install
npm run dev

Core Components Used

The application is built using the following Katlux components:

  • KGrid: For flexible layout and alignment of the form and calendar.
  • KPanel: To create titled containers for different sections of the UI.
  • KDatePicker: For selecting and displaying the active date in a masked format.
  • KCalendar: To visualize tasks on a monthly view with marked dates.
  • KTextbox, KTextarea, and KButton: For intuitive task entry.
1. The Template Structure

The application uses a split layout. The left side contains the task entry form, and the right side displays the calendar and the list of tasks for the selected day.

Vue
<template lang="pug">
NuxtLayout
	KGrid(align="stretch" flexChild=true)
		//- Left side: Task Entry Form
		KPanel(title="Add New Task")
			KGrid(direction="column" align="stretch")
				KDatePicker(
					v-model:maskedModel="pickerValue",
					:dateSelected="updateSelectedDay",
					:calendarDay="selectedDay",
					:calendarDefinedDays="definedDays",
					format="YYYY-MM-DD"
				)
				KTextbox(v-model="newTaskLabel", placeholder="Task Title (e.g. Finish the project)")
				KTextarea(v-model="newTaskDescription", placeholder="Description (Some additional details...)")
				KButton(type="primary", size="medium" @click="addTask") Add Task

		//- Right side: Calendar & Events
		KGrid(align="stretch" flexChild=true)
			KPanel(title="Monthly View")
				KCalendar(
					view="month",
					:viewCount="1",
					:day="selectedDay",
					:monthSelectedDay="selectedDay",
					:dayClicked="dayClicked",
					:definedDays="definedDays",
					:markedDays="markedDays"
				)
			//- Bottom section: List of Events for the selected day
			KPanel(title="Tasks for Today")
				div(v-if="filteredTasks.length === 0")
					p No tasks for the selected date.
				KGrid(v-else direction="column")
					KGrid(v-for="task in filteredTasks" :key="task.label")
						KGrid(direction="column")
							h3 {{ task.label }}
							p {{ task.description }}
</template>
2. The Application Logic

The logic handles date formatting, task filtering based on the selected day (including recurring events like birthdays), and adding new tasks to the reactive markedDays array.

TypeScript
<script setup lang="ts">
import { ref, computed, watch } from 'vue'

// 1. Define custom styled recurring days (e.g. Weekends)
const definedDays = [
	{
		date: {
			startDate: '2025-01-01',
			endDate: '2025-12-31',
			recurringRules: [{ unit: 'month', daysOfWeek: ['Sat', 'Sun'], unitOccuranceStartIndex: 0 }]
		},
		dayStyle: { isOffday: true, isUnderlined: true, isDisabled: true }
	}
]

// 2. Highlight specific events or dates
const markedDays = ref([
	{
		date: { recurringRules: [{ unit: 'year', daysOfYear: [{ month: 1, day: 30 }] }] },
		type: 'error',
		label: 'My Birthday',
		description: 'This is my birthday'
	}
])

const selectedDay = ref(new Date(2026, 2, 28))
selectedDay.value.setHours(0, 0, 0, 0)
const newTaskLabel = ref('')
const newTaskDescription = ref('')

const formatDate = (date) => {
	if (!date) return ''
	const year = date.getFullYear()
	const month = String(date.getMonth() + 1).padStart(2, '0')
	const day = String(date.getDate()).padStart(2, '0')
	return `${year}-${month}-${day}`
}

const pickerValue = computed({
	get: () => formatDate(selectedDay.value),
	set: (val) => updateSelectedDay(val)
})

const filteredTasks = computed(() => {
	const currentStr = formatDate(selectedDay.value)
	return markedDays.value.filter(d => {
		if (d.date.startDate === currentStr) return true
		if (d.date.recurringRules && d.date.recurringRules[0].unit === 'year') {
			const rule = d.date.recurringRules[0]
			if (rule.daysOfYear && rule.daysOfYear.length > 0) {
				const birthday = rule.daysOfYear[0]
				const birthdayStr = `-${String(birthday.month).padStart(2, '0')}-${String(birthday.day).padStart(2, '0')}`
				return currentStr.endsWith(birthdayStr)
			}
		}
		return false
	})
})

const addTask = () => {
	if (!newTaskLabel.value || newTaskLabel.value.trim() === '') return
	setTimeout(() => {
		const dateStr = formatDate(selectedDay.value)
		markedDays.value = [
			...markedDays.value,
			{
				date: { startDate: dateStr, endDate: dateStr },
				type: 'success',
				label: newTaskLabel.value,
				description: newTaskDescription.value
			}
		]
		newTaskLabel.value = ''
		newTaskDescription.value = ''
	}, 100)
}

const updateSelectedDay = (val) => {
	if (!val) return
	let newDate = null
	if (val instanceof Date) {
		newDate = new Date(val)
	} else if (typeof val === 'string') {
		const parts = val.split('-')
		if (parts.length === 3) {
			newDate = new Date(parseInt(parts[0]), parseInt(parts[1]) - 1, parseInt(parts[2]))
		}
	}
	if (newDate && selectedDay.value.getTime() !== newDate.getTime()) {
		selectedDay.value = newDate
	}
}

const dayClicked = (date, marked) => {
	updateSelectedDay(date)
}
</script>

Summary

The TaskRecorder demonstrates how quickly you can build a functional, beautiful application by combining Katlux components. It leverages Nuxt 3's powerful features along with Katlux's design system to provide a premium user experience with minimal effort.